{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "154b2d0e-f7ce-453b-b3b7-eda0666a9795",
   "metadata": {},
   "source": [
    "# Building RAG-based LLM Applications for Production"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de569042-32c7-4bea-a1ef-f0e41e260645",
   "metadata": {},
   "source": [
    "- **Blog post**: https://www.anyscale.com/blog/a-comprehensive-guide-for-building-rag-based-llm-applications-part-1\n",
    "- **GitHub repository**: https://github.com/ray-project/llm-applications\n",
    "- **Anyscale Endpoints**: https://endpoints.anyscale.com/ (serve + fine-tune LLMs)\n",
    "- **Ray documentation**: https://docs.ray.io/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68a962bf-4bb5-47ff-a667-89d493b1eeed",
   "metadata": {
    "tags": []
   },
   "source": [
    "In this guide, we will learn how to:\n",
    "\n",
    "- 💻 Develop a retrieval augmented generation (RAG) based LLM application from scratch.\n",
    "- 🚀 Scale the major workloads (load, chunk, embed, index, serve, etc.) across multiple workers with different compute resources.\n",
    "- ✅ Evaluate different configurations of our application to optimize for both per-component (ex. retrieval_score) and overall performance (quality_score).\n",
    "- 🔀 Implement a hybrid agent routing approach b/w OSS and closed LLMs to create the most performant and cost effective application.\n",
    "- 📦 Serve the application in a highly scalable and available manner.\n",
    "- 💡 Learn how methods like fine-tuning, prompt engineering, lexical search, reranking, data flywheel, etc. impact our application's performance."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6b842ed-9c65-488f-a5b3-fcbced58c2c5",
   "metadata": {},
   "source": [
    "# Overview"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c09a1b69-8386-4043-846a-86b5396090af",
   "metadata": {},
   "source": [
    "Large language models (LLMs) have undoubtedly changed the way we interact with information. However, they come with their fair share of limitations as to what we can ask of them. Base LLMs (ex. Llama-2-70b, gpt-4, etc.) are only aware of the information that they've been trained on and will fall short when we require them to know information beyond that. Retrieval augmented generation (RAG) based LLM applications address this exact issue and extend the utility of LLMs and their generative reasoning abilities to our unique datasets. \n",
    "\n",
    "In this guide, we're going to build a RAG-based LLM application where we will incorporate external data sources to augment our LLM’s capabilities. Specifically, we will be building an assistant that can answer questions about [Ray](https://github.com/ray-project/ray) — a Python framework for productionizing and scaling ML workloads. The goal here is to make it easier for developers to adopt Ray, but also, as we'll see in this guide, to help improve our Ray documentation itself and provide a foundation for other LLM applications. We’ll also share challenges we faced along the way and how we overcame them.\n",
    "\n",
    "**Note**: We have generalized this entire guide so that it can easily be extended to build RAG-based LLM applications on top of your own data.\n",
    "\n",
    "<img width=\"500\" src=\"https://images.ctfassets.net/xjan103pcp94/4PX0l1ruKqfH17YvUiMFPw/c60a7a665125cb8056bebcc146c23b76/image8.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc5bc988-231a-4aa4-8d9d-2192488e1724",
   "metadata": {},
   "source": [
    "Besides just building our LLM application, we’re also going to be focused on scaling and serving it in production. Unlike traditional machine learning, or even supervised deep learning, scale is a bottleneck for LLM applications from the very beginning. Large datasets, models, compute intensive workloads, serving requirements, etc. We’ll develop our application to be able to handle any scale as the world around us continues to grow. We’re also going to be focused on evaluation and performance. Our application involves many moving pieces: embedding models, chunking logic, the LLM itself, etc. and so it's important that we experiment with different configurations to optimize for the best quality responses. However, it's non-trivial to evaluate and quantitatively compare different configurations for a generative task. We’re going to break down evaluation of individual parts of our application (retrieval given query, generation given source), also assess the overall performance (end-to-end generation) and share findings towards an optimized configuration.\n",
    "\n",
    "**Note**: We'll be experimenting with different LLMs (OpenAI, Llama, etc.) in this guide. You will need [OpenAI credentials](https://platform.openai.com/account/api-keys) to access [ChatGPT models](https://platform.openai.com/docs/models/) and [Anyscale Endpoints](https://endpoints.anyscale.com/) (hosted/private endpoints available) to serve + fine-tune OSS LLMs."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35af14d4-478a-418b-a738-b17012188779",
   "metadata": {},
   "source": [
    "# Set up"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dde6f87c-c5c2-4374-b256-f6d17205d402",
   "metadata": {},
   "source": [
    "We're going to start by setting up our base imports, directories and initializing Ray with credentials. We'll be using [Ray](https://docs.ray.io/) to easily scale our workloads with minimal changes to our code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b08502d8-e9a9-4a50-acd9-76f77b18ada6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import ray"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "633996c3-45b4-4ac6-961d-56b0df9156c0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import sys; sys.path.append(\"..\")\n",
    "import warnings; warnings.filterwarnings(\"ignore\")\n",
    "from dotenv import load_dotenv; load_dotenv()\n",
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "591d064d-e843-4b5a-bb89-0fcfb16a5045",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from rag.config import ROOT_DIR"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f36dc38-f797-4db9-9979-2450764679aa",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-04-23 15:30:03,651\tINFO worker.py:1458 -- Connecting to existing Ray cluster at address: 10.0.20.178:6379...\n",
      "2024-04-23 15:30:03,691\tINFO worker.py:1633 -- Connected to Ray cluster. View the dashboard at \u001b[1m\u001b[32mhttps://session-5ljni527x7edt2q6px7nuaejct.i.anyscaleuserdata-staging.com \u001b[39m\u001b[22m\n",
      "2024-04-23 15:30:03,822\tINFO packaging.py:518 -- Creating a file package for local directory '/home/ray/default/notebooks/..'.\n",
      "2024-04-23 15:30:04,016\tINFO packaging.py:346 -- Pushing file package 'gcs://_ray_pkg_8bbd3b9b436028ec.zip' (47.46MiB) to Ray cluster...\n",
      "2024-04-23 15:30:04,163\tINFO packaging.py:359 -- Successfully pushed file package 'gcs://_ray_pkg_8bbd3b9b436028ec.zip'.\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3508712689e04d298da81a3fe2244502",
       "version_major": 2,
       "version_minor": 0
      },
      "text/html": [
       "<div class=\"lm-Widget p-Widget lm-Panel p-Panel jp-Cell-outputWrapper\">\n",
       "    <div style=\"margin-left: 50px;display: flex;flex-direction: row;align-items: center\">\n",
       "        <div class=\"jp-RenderedHTMLCommon\" style=\"display: flex; flex-direction: row;\">\n",
       "  <svg viewBox=\"0 0 567 224\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 3em;\">\n",
       "    <g clip-path=\"url(#clip0_4338_178347)\">\n",
       "        <path d=\"M341.29 165.561H355.29L330.13 129.051C345.63 123.991 354.21 112.051 354.21 94.2307C354.21 71.3707 338.72 58.1807 311.88 58.1807H271V165.561H283.27V131.661H311.8C314.25 131.661 316.71 131.501 319.01 131.351L341.25 165.561H341.29ZM283.29 119.851V70.0007H311.82C331.3 70.0007 342.34 78.2907 342.34 94.5507C342.34 111.271 331.34 119.861 311.82 119.861L283.29 119.851ZM451.4 138.411L463.4 165.561H476.74L428.74 58.1807H416L367.83 165.561H380.83L392.83 138.411H451.4ZM446.19 126.601H398L422 72.1407L446.24 126.601H446.19ZM526.11 128.741L566.91 58.1807H554.35L519.99 114.181L485.17 58.1807H472.44L514.01 129.181V165.541H526.13V128.741H526.11Z\" fill=\"var(--jp-ui-font-color0)\"/>\n",
       "        <path d=\"M82.35 104.44C84.0187 97.8827 87.8248 92.0678 93.1671 87.9146C98.5094 83.7614 105.083 81.5067 111.85 81.5067C118.617 81.5067 125.191 83.7614 130.533 87.9146C135.875 92.0678 139.681 97.8827 141.35 104.44H163.75C164.476 101.562 165.622 98.8057 167.15 96.2605L127.45 56.5605C121.071 60.3522 113.526 61.6823 106.235 60.3005C98.9443 58.9187 92.4094 54.9203 87.8602 49.0574C83.3109 43.1946 81.0609 35.8714 81.5332 28.4656C82.0056 21.0599 85.1679 14.0819 90.4252 8.8446C95.6824 3.60726 102.672 0.471508 110.08 0.0272655C117.487 -0.416977 124.802 1.86091 130.647 6.4324C136.493 11.0039 140.467 17.5539 141.821 24.8501C143.175 32.1463 141.816 39.6859 138 46.0505L177.69 85.7505C182.31 82.9877 187.58 81.4995 192.962 81.4375C198.345 81.3755 203.648 82.742 208.33 85.3976C213.012 88.0532 216.907 91.9029 219.616 96.5544C222.326 101.206 223.753 106.492 223.753 111.875C223.753 117.258 222.326 122.545 219.616 127.197C216.907 131.848 213.012 135.698 208.33 138.353C203.648 141.009 198.345 142.375 192.962 142.313C187.58 142.251 182.31 140.763 177.69 138L138 177.7C141.808 184.071 143.155 191.614 141.79 198.91C140.424 206.205 136.44 212.75 130.585 217.313C124.731 221.875 117.412 224.141 110.004 223.683C102.596 223.226 95.6103 220.077 90.3621 214.828C85.1139 209.58 81.9647 202.595 81.5072 195.187C81.0497 187.779 83.3154 180.459 87.878 174.605C92.4405 168.751 98.9853 164.766 106.281 163.401C113.576 162.035 121.119 163.383 127.49 167.19L167.19 127.49C165.664 124.941 164.518 122.182 163.79 119.3H141.39C139.721 125.858 135.915 131.673 130.573 135.826C125.231 139.98 118.657 142.234 111.89 142.234C105.123 142.234 98.5494 139.98 93.2071 135.826C87.8648 131.673 84.0587 125.858 82.39 119.3H60C58.1878 126.495 53.8086 132.78 47.6863 136.971C41.5641 141.163 34.1211 142.972 26.7579 142.059C19.3947 141.146 12.6191 137.574 7.70605 132.014C2.79302 126.454 0.0813599 119.29 0.0813599 111.87C0.0813599 104.451 2.79302 97.2871 7.70605 91.7272C12.6191 86.1673 19.3947 82.5947 26.7579 81.6817C34.1211 80.7686 41.5641 82.5781 47.6863 86.7696C53.8086 90.9611 58.1878 97.2456 60 104.44H82.35ZM100.86 204.32C103.407 206.868 106.759 208.453 110.345 208.806C113.93 209.159 117.527 208.258 120.522 206.256C123.517 204.254 125.725 201.276 126.771 197.828C127.816 194.38 127.633 190.677 126.253 187.349C124.874 184.021 122.383 181.274 119.205 179.577C116.027 177.88 112.359 177.337 108.826 178.042C105.293 178.746 102.113 180.654 99.8291 183.44C97.5451 186.226 96.2979 189.718 96.3 193.32C96.2985 195.364 96.7006 197.388 97.4831 199.275C98.2656 201.163 99.4132 202.877 100.86 204.32ZM204.32 122.88C206.868 120.333 208.453 116.981 208.806 113.396C209.159 109.811 208.258 106.214 206.256 103.219C204.254 100.223 201.275 98.0151 197.827 96.97C194.38 95.9249 190.676 96.1077 187.348 97.4873C184.02 98.8669 181.274 101.358 179.577 104.536C177.879 107.714 177.337 111.382 178.041 114.915C178.746 118.448 180.653 121.627 183.439 123.911C186.226 126.195 189.717 127.443 193.32 127.44C195.364 127.443 197.388 127.042 199.275 126.259C201.163 125.476 202.878 124.328 204.32 122.88ZM122.88 19.4205C120.333 16.8729 116.981 15.2876 113.395 14.9347C109.81 14.5817 106.213 15.483 103.218 17.4849C100.223 19.4868 98.0146 22.4654 96.9696 25.9131C95.9245 29.3608 96.1073 33.0642 97.4869 36.3922C98.8665 39.7202 101.358 42.4668 104.535 44.1639C107.713 45.861 111.381 46.4036 114.914 45.6992C118.447 44.9949 121.627 43.0871 123.911 40.301C126.195 37.515 127.442 34.0231 127.44 30.4205C127.44 28.3772 127.038 26.3539 126.255 24.4664C125.473 22.5788 124.326 20.8642 122.88 19.4205ZM19.42 100.86C16.8725 103.408 15.2872 106.76 14.9342 110.345C14.5813 113.93 15.4826 117.527 17.4844 120.522C19.4863 123.518 22.4649 125.726 25.9127 126.771C29.3604 127.816 33.0638 127.633 36.3918 126.254C39.7198 124.874 42.4664 122.383 44.1635 119.205C45.8606 116.027 46.4032 112.359 45.6988 108.826C44.9944 105.293 43.0866 102.114 40.3006 99.8296C37.5145 97.5455 34.0227 96.2983 30.42 96.3005C26.2938 96.3018 22.337 97.9421 19.42 100.86ZM100.86 100.86C98.3125 103.408 96.7272 106.76 96.3742 110.345C96.0213 113.93 96.9226 117.527 98.9244 120.522C100.926 123.518 103.905 125.726 107.353 126.771C110.8 127.816 114.504 127.633 117.832 126.254C121.16 124.874 123.906 122.383 125.604 119.205C127.301 116.027 127.843 112.359 127.139 108.826C126.434 105.293 124.527 102.114 121.741 99.8296C118.955 97.5455 115.463 96.2983 111.86 96.3005C109.817 96.299 107.793 96.701 105.905 97.4835C104.018 98.2661 102.303 99.4136 100.86 100.86Z\" fill=\"#00AEEF\"/>\n",
       "    </g>\n",
       "    <defs>\n",
       "        <clipPath id=\"clip0_4338_178347\">\n",
       "            <rect width=\"566.93\" height=\"223.75\" fill=\"white\"/>\n",
       "        </clipPath>\n",
       "    </defs>\n",
       "  </svg>\n",
       "</div>\n",
       "\n",
       "        <table class=\"jp-RenderedHTMLCommon\" style=\"border-collapse: collapse;color: var(--jp-ui-font-color1);font-size: var(--jp-ui-font-size1);\">\n",
       "    <tr>\n",
       "        <td style=\"text-align: left\"><b>Python version:</b></td>\n",
       "        <td style=\"text-align: left\"><b>3.10.8</b></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "        <td style=\"text-align: left\"><b>Ray version:</b></td>\n",
       "        <td style=\"text-align: left\"><b>2.7.0</b></td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "    <td style=\"text-align: left\"><b>Dashboard:</b></td>\n",
       "    <td style=\"text-align: left\"><b><a href=\"http://session-5ljni527x7edt2q6px7nuaejct.i.anyscaleuserdata-staging.com\" target=\"_blank\">http://session-5ljni527x7edt2q6px7nuaejct.i.anyscaleuserdata-staging.com</a></b></td>\n",
       "</tr>\n",
       "\n",
       "</table>\n",
       "\n",
       "    </div>\n",
       "</div>\n"
      ],
      "text/plain": [
       "RayContext(dashboard_url='session-5ljni527x7edt2q6px7nuaejct.i.anyscaleuserdata-staging.com', python_version='3.10.8', ray_version='2.7.0', ray_commit='acb4a960947869e158a973c6c4bdf1aca2d66b10', protocol_version=None)"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Credentials\n",
    "ray.init(runtime_env={\n",
    "    \"env_vars\": {\n",
    "        \"OPENAI_API_BASE\": os.environ[\"OPENAI_API_BASE\"],\n",
    "        \"OPENAI_API_KEY\": os.environ[\"OPENAI_API_KEY\"], \n",
    "        \"ANYSCALE_API_BASE\": os.environ[\"ANYSCALE_API_BASE\"],\n",
    "        \"ANYSCALE_API_KEY\": os.environ[\"ANYSCALE_API_KEY\"],\n",
    "        \"DB_CONNECTION_STRING\": os.environ[\"DB_CONNECTION_STRING\"],\n",
    "    },\n",
    "    \"working_dir\": str(ROOT_DIR)\n",
    "})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "880b8660-e613-431f-a083-d4985711e8bf",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'CPU': 8.0,\n",
       " 'node:__internal_head__': 1.0,\n",
       " 'accelerator_type:A10G': 1.0,\n",
       " 'object_store_memory': 9540210278.0,\n",
       " 'node:10.0.20.178': 1.0,\n",
       " 'GPU': 1.0,\n",
       " 'memory': 34359738368.0}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ray.cluster_resources()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e3928f1-1404-430d-8633-45bb1a1c21d3",
   "metadata": {},
   "source": [
    "We've also created some mappings for the different embedding and language models we'll be developing with in our application:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab69df3c-bf18-4750-a680-4e488888c5e8",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from rag.config import EMBEDDING_DIMENSIONS, MAX_CONTEXT_LENGTHS, PRICING"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8ba8f76-7ae8-49c1-8907-151b1fecbaf8",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'thenlper/gte-base': 768,\n",
       " 'thenlper/gte-large': 1024,\n",
       " 'BAAI/bge-large-en': 1024,\n",
       " 'text-embedding-ada-002': 1536,\n",
       " 'gte-large-fine-tuned': 1024}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Embedding dimensions\n",
    "EMBEDDING_DIMENSIONS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b6d340fa-4a87-4cc8-8d74-c09aafd9d18d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'gpt-4': 8192,\n",
       " 'gpt-3.5-turbo': 4096,\n",
       " 'gpt-3.5-turbo-16k': 16384,\n",
       " 'gpt-4-1106-preview': 128000,\n",
       " 'meta-llama/Llama-2-7b-chat-hf': 4096,\n",
       " 'meta-llama/Llama-2-13b-chat-hf': 4096,\n",
       " 'meta-llama/Llama-2-70b-chat-hf': 4096,\n",
       " 'meta-llama/Llama-3-8b-chat-hf': 8192,\n",
       " 'meta-llama/Llama-3-70b-chat-hf': 8192,\n",
       " 'codellama/CodeLlama-34b-Instruct-hf': 16384,\n",
       " 'mistralai/Mistral-7B-Instruct-v0.1': 65536,\n",
       " 'mistralai/Mixtral-8x7B-Instruct-v0.1': 32768,\n",
       " 'mistralai/Mixtral-8x22B-Instruct-v0.1': 65536}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# LLM context lengths (1 token = 3/4 word)\n",
    "MAX_CONTEXT_LENGTHS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8b6c5ec8-70c6-44ea-9576-2b41a898acc0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'gpt-3.5-turbo': {'prompt': 1.5, 'sampled': 2},\n",
       " 'gpt-4': {'prompt': 30, 'sampled': 60},\n",
       " 'gpt-4-1106-preview': {'prompt': 10, 'sampled': 30},\n",
       " 'llama-2-7b-chat-hf': {'prompt': 0.15, 'sampled': 0.15},\n",
       " 'llama-2-13b-chat-hf': {'prompt': 0.25, 'sampled': 0.25},\n",
       " 'llama-2-70b-chat-hf': {'prompt': 1, 'sampled': 1},\n",
       " 'llama-3-8b-chat-hf': {'prompt': 0.15, 'sampled': 0.15},\n",
       " 'llama-3-70b-chat-hf': {'prompt': 1, 'sampled': 1},\n",
       " 'codellama-34b-instruct-hf': {'prompt': 1, 'sampled': 1},\n",
       " 'mistral-7b-instruct-v0.1': {'prompt': 0.15, 'sampled': 0.15},\n",
       " 'mixtral-8x7b-instruct-v0.1': {'prompt': 0.5, 'sampled': 0.5},\n",
       " 'mixtral-8x22b-instruct-v0.1': {'prompt': 0.9, 'sampled': 0.9}}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Anyscale pricing\n",
    "PRICING"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bab7f73e-bdd1-4c87-93d0-92ece2344432",
   "metadata": {},
   "source": [
    "# Data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00a1ed16-0f0d-4be1-951a-21a8cde1bfe9",
   "metadata": {},
   "source": [
    "Before we can start building our RAG application, we need to first create our vector DB that will contain our processed data sources.\n",
    "\n",
    "<img width=\"1000\" src=\"https://images.ctfassets.net/xjan103pcp94/3q5HUANQ4kS0V23cgEP0JF/ef3b62c5bc5c5c11b734fd3b73f6ea28/image3.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c503edd-963a-4ec3-9182-39f7afc44153",
   "metadata": {},
   "source": [
    "## Load data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2301b6f6-7402-4eeb-9975-7c28194b6914",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pathlib import Path\n",
    "from rag.config import EFS_DIR"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f82562f-2b5d-4e8d-9716-b0da8670e8bf",
   "metadata": {},
   "source": [
    "We need to first download the [Ray documentation](https://docs.ray.io/) to a directory:\n",
    "\n",
    "```bash\n",
    "# Move to the llm-applications/ directory.\n",
    "export EFS_DIR=$(python -c \"from rag.config import EFS_DIR; print(EFS_DIR)\")\n",
    "wget -e robots=off --recursive --no-clobber --page-requisites \\\n",
    "  --html-extension --convert-links --restrict-file-names=windows \\\n",
    "  --domains docs.ray.io --no-parent --accept=html --retry-on-http-error=429 \\\n",
    "  -P $EFS_DIR https://docs.ray.io/en/master/\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77456cf7-fe2b-4884-bfc2-99a2b4ffba1a",
   "metadata": {},
   "source": [
    "We’re going to then load our docs contents into a [Ray Dataset](https://docs.ray.io/en/latest/data/data.html) so that we can perform operations at scale on them (ex. embed, index, etc.). With large data sources, models and application serving needs, scale is a day-1 priority for LLM applications. We want to build our applications in such a way that they can scale as our needs grow without us having to change our code later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bba6b43b-ea82-4c21-a885-57178cec3b44",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3053 documents\n"
     ]
    }
   ],
   "source": [
    "# Ray dataset\n",
    "DOCS_DIR = Path(EFS_DIR, \"docs.ray.io/en/master/\")\n",
    "ds = ray.data.from_items([{\"path\": path} for path in DOCS_DIR.rglob(\"*.html\") if not path.is_dir()])\n",
    "print(f\"{ds.count()} documents\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba9edff6-6dbf-4037-9675-ae05cd3eb7a7",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Sections"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f13f0dc-7f7a-4132-93e4-dc69aab164a2",
   "metadata": {},
   "source": [
    "Now that we have a dataset of all the paths to the html files, we're going to develop some functions that can appropriately extract the content from these files. We want to do this in a generalized manner so that we can perform this extraction across all of our docs pages (and so you can use it for your own data sources). Our process is to first identify the sections in our html page and then extract the text in between them. We save all of this into a list of dictionaries that map the text within a section to a specific url with a section anchor id.\n",
    "\n",
    "<img width=\"800\" src=\"https://images.ctfassets.net/xjan103pcp94/1eFnKmG5xqPIFtPupZ327X/f6152723e18322b90aaa8be5d2d5a6e4/image5.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f72dedb6-63d1-414f-b973-6a1009fcaf74",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from rag.data import extract_sections"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a8033de-2508-4030-a9a1-d6c792d5542a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'source': 'https://docs.ray.io/en/master/rllib/rllib-env.html#environments',\n",
       " 'text': '\\nEnvironments#\\nRLlib works with several different types of environments, including Farama-Foundation Gymnasium, user-defined, multi-agent, and also batched environments.\\nTip\\nNot all environments work with all algorithms. Check out the algorithm overview for more information.\\n'}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sample_html_fp = Path(EFS_DIR, \"docs.ray.io/en/master/rllib/rllib-env.html\")\n",
    "extract_sections({\"path\": sample_html_fp})[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24f4ed64-2dd8-43c2-9831-cdf9d6d8004c",
   "metadata": {},
   "source": [
    "We can apply this extraction process (extract_section) in parallel to all the file paths in our dataset with just one line using Ray Data's [flat_map](https://docs.ray.io/en/latest/data/api/doc/ray.data.Dataset.flat_map.html):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4739d56-ddfe-42e5-9113-3d83d737999a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-04-23 15:35:55,700\tINFO streaming_executor.py:93 -- Executing DAG InputDataBuffer[Input] -> TaskPoolMapOperator[FlatMap(extract_sections)]\n",
      "2024-04-23 15:35:55,701\tINFO streaming_executor.py:94 -- Execution config: ExecutionOptions(resource_limits=ExecutionResources(cpu=None, gpu=None, object_store_memory=None), locality_with_output=False, preserve_order=False, actor_locality_enabled=True, verbose_progress=False)\n",
      "2024-04-23 15:35:55,701\tINFO streaming_executor.py:96 -- Tip: For detailed progress reporting, run `ray.data.DataContext.get_current().execution_options.verbose_progress = True`\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Running 0:   0%|          | 0/200 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "5597"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Extract sections\n",
    "sections_ds = ds.flat_map(extract_sections)\n",
    "sections_ds.count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39641fc6-adb2-4a6a-a8dd-f0433db814ff",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-04-23 15:37:56,479\tWARNING plan.py:567 -- Warning: The Ray cluster currently does not have any available CPUs. The Dataset job will hang unless more CPUs are freed up. A common reason is that cluster resources are used by Actors or Tune trials; see the following link for more details: https://docs.ray.io/en/master/data/dataset-internals.html#datasets-and-tune\n"
     ]
    }
   ],
   "source": [
    "section_lengths = []\n",
    "for section in sections_ds.take_all():\n",
    "    section_lengths.append(len(section[\"text\"]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c144ef2-ffa5-49ed-9bb3-60a50223854c",
   "metadata": {},
   "source": [
    "## Chunk data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78c103aa-9b34-4d7b-8b42-78737eb0888d",
   "metadata": {},
   "source": [
    "We now have a list of sections (with text and source of each section) but we shouldn't directly use this as context to our RAG application just yet. The text lengths of each section are all varied and many are quite large chunks. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b07383c-b898-4f4a-b008-837a8bf83015",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABAcAAAEpCAYAAADvWkujAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABi7UlEQVR4nO3deVxU9f4/8NfMwDAszgAiDCgKqaWEae643VISy+xa3puWLV/z6s2gXNq0zKXlWt5rmWXacst+10yv3bRcQklNU3FDURE1U9wZUIEZQNaZ8/uD5sjADMzADLO9no8Hjwec8+HM58zZ3+fzeX8kgiAIICIiIiIiIiKvJXV2BYiIiIiIiIjIuRgcICIiIiIiIvJyDA4QEREREREReTkGB4iIiIiIiIi8HIMDRERERERERF6OwQEiIiIiIiIiL8fgABEREREREZGXY3CAiIiIiIiIyMsxOEBERERERETk5RgcICIiomaRSCSYN29ei3/uvHnzIJFIWvxzbfXLL79AIpHgu+++c3ZViIiILGJwgIiIyM0cP34cf/nLX9ChQwcoFAq0bdsW9913Hz766COHfebmzZudEgBwJ6tWrcLixYudXQ0iIqImYXCAiIjIjezduxe9e/fG0aNHMWnSJHz88cf429/+BqlUig8//NBhn7t582bMnz/f7LyysjLMnj3bYZ/tLhgcICIid+bj7AoQERGR9d555x2oVCocPHgQwcHBJvPy8/OdUieFQuGUzyUiIiL7YcsBIiIiN3L27Fnceeed9QIDABAeHl5v2sqVK9GrVy/4+/sjNDQU48aNw6VLl+qV279/Px544AGEhIQgMDAQd911l9gS4f/+7/+wdOlSADX5BYw/RuZyDhw5cgT3338/lEolgoKCMGzYMOzbt8+kzIoVKyCRSLBnzx7MmDEDbdq0QWBgIB5++GFcu3bN1q/GpnW+5557EB8fj+zsbNx7770ICAhA27ZtsXDhwnrLu3DhAh566CEEBgYiPDwc06dPx5YtWyCRSPDLL7+Iy9u0aRMuXLggfj8xMTEmyzEYDHjnnXfQrl07KBQKDBs2DL///rtJmTNnzmDMmDFQq9VQKBRo164dxo0bB61W2+Tvg4iIyBpsOUBERORGOnTogPT0dGRlZSE+Pr7Bsu+88w7eeOMNPProo/jb3/6Ga9eu4aOPPsKQIUNw5MgRMcCQlpaGBx98EJGRkZg6dSrUajVOnjyJjRs3YurUqfj73/+Oq1evIi0tDf/5z38areOJEycwePBgKJVKvPLKK/D19cWnn36Ke+65Bzt37kS/fv1Myj///PMICQnB3Llzcf78eSxevBgpKSlYs2aNzd+PtesMAIWFhRgxYgQeeeQRPProo/juu+/w6quvolu3brj//vsBAKWlpRg6dChyc3PF72bVqlXYsWOHyee+/vrr0Gq1uHz5Mj744AMAQFBQkEmZd999F1KpFC+99BK0Wi0WLlyI8ePHY//+/QCAyspKJCUloaKiAs8//zzUajWuXLmCjRs3oqioCCqVyubvg4iIyGoCERERuY2tW7cKMplMkMlkQkJCgvDKK68IW7ZsESorK03KnT9/XpDJZMI777xjMv348eOCj4+POL26ulqIjY0VOnToIBQWFpqUNRgM4u/JycmCpdsGAMLcuXPFv0ePHi3I5XLh7Nmz4rSrV68KrVq1EoYMGSJO++qrrwQAQmJioslnTZ8+XZDJZEJRUVGD38XcuXNN6mTtOguCIPzpT38SAAj/7//9P3FaRUWFoFarhTFjxojTFi1aJAAQ1q9fL04rKysTunTpIgAQduzYIU4fOXKk0KFDh3r13LFjhwBA6Nq1q1BRUSFO//DDDwUAwvHjxwVBEIQjR44IAIS1a9c2uN5ERESOwG4FREREbuS+++5Deno6HnroIRw9ehQLFy5EUlIS2rZtix9//FEs9/3338NgMODRRx/F9evXxR+1Wo3OnTuLb76PHDmCnJwcTJs2rV5XhaYME6jX67F161aMHj0at912mzg9MjISjz/+OHbv3g2dTmfyP5MnTzb5rMGDB0Ov1+PChQs2fba162wUFBSEJ554QvxbLpejb9++OHfunDgtNTUVbdu2xUMPPSROUygUmDRpkk11A4AJEyZALpeLfw8ePBgAxM8ztgzYsmULbt68afPyiYiImoPBASIiIjfTp08ffP/99ygsLMSBAwcwa9YsFBcX4y9/+Quys7MB1PRdFwQBnTt3Rps2bUx+Tp48KSYvPHv2LAA02kXBWteuXcPNmzdxxx131JvXtWtXGAyGev3/27dvb/J3SEgIgJpm/7awdp2N2rVrVy8AEhISYvK5Fy5cQMeOHeuV69Spk011Axpfz9jYWMyYMQNffPEFwsLCkJSUhKVLlzLfABER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iuTkZPTq1QthYWGYM2cOJk+eLJYZMGAAVq1ahdmzZ+O1115D586dsX79esTHx4tlrKkLETkPBysgIiIiImo6iSBwALCWYu34kkRknVMaHUYs/hUAcPYfD0AmlTi5RkRERERErsXa51CXzTlARERERERERC2DwQEiIiIiIiIiL8fgABEREREREZGXY3CAiDwC06cQERERETUdgwNEREREREREXo7BASIiIiIiIiIvx+AAERERERERkZdjcICIiIiIiIjIyzE4QEQegekIiYiIiIiajsEBIiIiIiIiIi/H4AARERERERGRl2NwgIiIiIiIiMjLMThARERERERE5OUYHCAiIiIiIiLycgwOEJFHEDhcARERERFRkzE4QEREREREROTlGBwgIiIiIiIi8nIMDhARERERERF5OR9nV4CIiIiIyJXoDQIO5BQgv7gc4a0U6BsbCplU4uxqERE5FIMDROQRBDAjIRERNV9qVi7mb8hGrrZcnBapUmDuqDiMiI90Ys2IiByL3QqIiIiIiFATGJiy8rBJYAAANNpyTFl5GKlZuU6qGRGR4zE4QEREREReT28QMH9Dttl2aMZp8zdkQ29gSzUi8kwMDhARERGR1zuQU1CvxUBtAoBcbTkO5BS0XKWIiFoQgwNERERE5PXyiy0HBppSjojI3TA4QERuS2DLTiIispPwVgq7liMicjcMDhCRR2CggIiImqNvbCgiVQpYGrBQgppRC/rGhrZktYiIWgyDA0TktiQccpqIiOxEJpVg7qg4AKgXIDD+PXdUHGRSXnyIyDMxOEBEREREBGBEfCSWPdETapVp1wG1SoFlT/TEiPhIJ9WMiMjxXDo4oNfr8cYbbyA2Nhb+/v7o2LEj3nrrLQi12g8LgoA5c+YgMjIS/v7+SExMxJkzZ0yWU1BQgPHjx0OpVCI4OBgTJ05ESUmJSZljx45h8ODBUCgUiI6OxsKFC+vVZ+3atejSpQsUCgW6deuGzZs3O2bFiYiIiMgpRsRHYverQ02m7X51KAMDROTxXDo48N5772HZsmX4+OOPcfLkSbz33ntYuHAhPvroI7HMwoULsWTJEixfvhz79+9HYGAgkpKSUF5+K5Ps+PHjceLECaSlpWHjxo3YtWsXJk+eLM7X6XQYPnw4OnTogIyMDPzzn//EvHnz8Nlnn4ll9u7di8ceewwTJ07EkSNHMHr0aIwePRpZWVkt82UQERERUYuo23WAXQmIyBtIBMF103g9+OCDiIiIwL///W9x2pgxY+Dv74+VK1dCEARERUXhxRdfxEsvvQQA0Gq1iIiIwIoVKzBu3DicPHkScXFxOHjwIHr37g0ASE1NxQMPPIDLly8jKioKy5Ytw+uvvw6NRgO5XA4AmDlzJtavX49Tp04BAMaOHYvS0lJs3LhRrEv//v3Ro0cPLF++3Kr10el0UKlU0Gq1UCqVdvmOiLzZKY0OIxb/WvP7WyOg8JU5uUZEROQpYmZuEn8//+5IJ9aEiKh5rH0OdemWAwMGDMC2bdvw22+/AQCOHj2K3bt34/777wcA5OTkQKPRIDExUfwflUqFfv36IT09HQCQnp6O4OBgMTAAAImJiZBKpdi/f79YZsiQIWJgAACSkpJw+vRpFBYWimVqf46xjPFzzKmoqIBOpzP5ISIiIiIiInI1Ps6uQENmzpwJnU6HLl26QCaTQa/X45133sH48eMBABqNBgAQERFh8n8RERHiPI1Gg/DwcJP5Pj4+CA0NNSkTGxtbbxnGeSEhIdBoNA1+jjkLFizA/PnzbV1tIiIiIiIiohbl0i0H/vvf/+Kbb77BqlWrcPjwYXz99df417/+ha+//trZVbPKrFmzoNVqxZ9Lly45u0pERERERERE9bh0y4GXX34ZM2fOxLhx4wAA3bp1w4ULF7BgwQI8/fTTUKvVAIC8vDxERt7KIJuXl4cePXoAANRqNfLz802WW11djYKCAvH/1Wo18vLyTMoY/26sjHG+OX5+fvDz87N1tYmIiIiIiIhalEu3HLh58yakUtMqymQyGAwGAEBsbCzUajW2bdsmztfpdNi/fz8SEhIAAAkJCSgqKkJGRoZYZvv27TAYDOjXr59YZteuXaiqqhLLpKWl4Y477kBISIhYpvbnGMsYP4eInMt1U6sSEREREbk+lw4OjBo1Cu+88w42bdqE8+fPY926dXj//ffx8MMPAwAkEgmmTZuGt99+Gz/++COOHz+Op556ClFRURg9ejQAoGvXrhgxYgQmTZqEAwcOYM+ePUhJScG4ceMQFRUFAHj88cchl8sxceJEnDhxAmvWrMGHH36IGTNmiHWZOnUqUlNTsWjRIpw6dQrz5s3DoUOHkJKS0uLfCxEREREREZE9uXS3go8++ghvvPEGnnvuOeTn5yMqKgp///vfMWfOHLHMK6+8gtLSUkyePBlFRUUYNGgQUlNToVAoxDLffPMNUlJSMGzYMEilUowZMwZLliwR56tUKmzduhXJycno1asXwsLCMGfOHEyePFksM2DAAKxatQqzZ8/Ga6+9hs6dO2P9+vWIj49vmS+DiIiIiIiIyEEkgsDGuC3F2vElicg6pzQ6jFj8KwDg5Jsj4C+XOblGRETkKWJmbhJ/P//uSCfWhIioeax9DnXpbgVERERERERE5HgMDhCR22K7JyIiIiIi+2BwgIg8ggBGCoiIiIiImorBASJyWxKJs2tAREREROQZGBwgIiIiIiIi8nIMDhARERERERF5OQYHiIiIiIiIiLwcgwNE5BE4cgERERERUdMxOEBERERERETk5RgcICIiIiIiIvJyDA4QEREREREReTkGB4iIiIiIiIi8HIMDROQRmI+QiIiIiKjpGBwgIiIiIiIi8nIMDhARERERERF5OQYHiIiIiIiIiLwcgwNEREREREREXq7ZwQGdTof169fj5MmT9qgPEZHVBGYhJCIiIiKyC5uDA48++ig+/vhjAEBZWRl69+6NRx99FHfddRf+97//2b2CRETWEBgpICIiIiJqMpuDA7t27cLgwYMBAOvWrYMgCCgqKsKSJUvw9ttv272CRESWSCTOrgERERERkWewOTig1WoRGhoKAEhNTcWYMWMQEBCAkSNH4syZM3avIBERERERERE5ls3BgejoaKSnp6O0tBSpqakYPnw4AKCwsBAKhcLuFSQiIiIiIiIix/Kx9R+mTZuG8ePHIygoCB06dMA999wDoKa7Qbdu3exdPyIiIiIiIiJyMJuDA8899xz69euHixcv4r777oNUWtP44LbbbmPOASJyGqYjJCIiIiJqOpu6FVRVVaFjx44ICAjAww8/jKCgIHHeyJEjMXDgQLtXkIiIiIiIiIgcy6bggK+vL8rLyx1VFyIiIiIiIiJyApsTEiYnJ+O9995DdXW1I+pDRERERERERC3M5pwDBw8exLZt27B161Z069YNgYGBJvO///57u1WOiIiIiIiIiBzP5pYDwcHBGDNmDJKSkhAVFQWVSmXyY29XrlzBE088gdatW8Pf3x/dunXDoUOHxPmCIGDOnDmIjIyEv78/EhMTcebMGZNlFBQUYPz48VAqlQgODsbEiRNRUlJiUubYsWMYPHgw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OnVCampqvek//fQTbrvtNrtUiojIGkxISERERERkH1aNVlDbjBkzkJKSgmvXrmHo0KEAgG3btmHRokVYvHixvetHRERERERERA5mc3DgmWeeQUVFBd555x289dZbAICYmBgsW7YMTz31lN0rSERERETUHHqDgAM5BcgvLkd4KwX6xoZCJmXzMyKi2mwODgDAlClTMGXKFFy7dg3+/v4ICgqyd72IiIiIiJotNSsX8zdkI1dbLk6LVCkwd1QcRsRHOrFmRESuxeacA7W1adOGgQEicgkChy4gIqI6UrNyMWXlYZPAAABotOWYsvIwUrNynVQzIiLX06zgABERERGRK9IbBMzfkG12NBvjtPkbsqE3MLhMRAQwOEBEREREHuhATkG9FgO1CQByteU4kFPQcpUiInJhDA4QERERkcfJL7YcGGhKOSIiT8fgABERERF5nPBWCruWIyLydE0KDqSkpKCggE2wiMi5aucgPHS+AD9kXkH62RvsP0pEROgbG4pIlQKWBiyUoGbUgr6xoS1ZLSIil2V1cODy5cvi76tWrUJJSQkAoFu3brh06ZL9a0ZEZIO/rzyMqasz8djn+zDove3MQE1E5OVkUgnmjooDgHoBAuPfc0fFQSa1FD4gIvIuVgcHunTpgg4dOuDxxx9HeXm5GBA4f/48qqqqHFZBIiJL9vx+3ex0DlFFREQAMCI+Esue6Am1yrTrgFqlwLInemJEfKSTakZE5HqsDg4UFRVh7dq16NWrFwwGAx544AHcfvvtqKiowJYtW5CXl+fIehIRmdAbBCzfedbsPA5RRURERiPiI7H71aHi368/0BW7Xx3KwAARUR1WBweqqqrQt29fvPjii/D398eRI0fw1VdfQSaT4csvv0RsbCzuuOMOR9aViEh0IKcA10sqLc7nEFVERGRUu+tAXJSSXQmIiMzwsbZgcHAwevTogYEDB6KyshJlZWUYOHAgfHx8sGbNGrRt2xYHDx50ZF2JiEQcooqIiIiIyH6sbjlw5coVzJ49G35+fqiurkavXr0wePBgVFZW4vDhw5BIJBg0aJAj64p3330XEokE06ZNE6eVl5cjOTkZrVu3RlBQEMaMGVOvi8PFixcxcuRIBAQEIDw8HC+//DKqq6tNyvzyyy/o2bMn/Pz80KlTJ6xYsaLe5y9duhQxMTFQKBTo168fDhw44IjVJCIr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",
      "text/plain": [
       "<Figure size 1200x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot\n",
    "plt.figure(figsize=(12, 3))\n",
    "plt.plot(section_lengths, marker='o')\n",
    "plt.title(\"Section lengths\")\n",
    "plt.ylabel(\"# chars\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfa7564d-fce5-4807-8bf2-de9f93c6f994",
   "metadata": {},
   "source": [
    "If we were to use these large sections, then we'd be inserting a lot of noisy/unwanted context and because all LLMs have a maximum context length, we wouldn't be able to fit too much other relevant context. So instead, we're going to split the text within each section into smaller chunks. Intuitively, smaller chunks will encapsulate single/few concepts and will be less noisy compared to larger chunks. We're going to choose some typical text splitting values (ex. chunk_size=300) to create our chunks for now but we'll be experimenting with a wider range of values later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97ca89ce-97dd-4462-8dfb-e06154c56ea2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from functools import partial\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6b6337e6-07b2-459d-a666-45de3aa945c4",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Text splitter\n",
    "chunk_size = 300\n",
    "chunk_overlap = 50\n",
    "text_splitter = RecursiveCharacterTextSplitter(\n",
    "    separators=[\"\\n\\n\", \"\\n\", \" \", \"\"],\n",
    "    chunk_size=chunk_size,\n",
    "    chunk_overlap=chunk_overlap,\n",
    "    length_function=len)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca5208da-7196-49b5-9943-c0c9f0ab13b5",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-04-23 15:37:56,978\tINFO dataset.py:2380 -- Tip: Use `take_batch()` instead of `take() / show()` to return records in pandas or numpy batch format.\n",
      "2024-04-23 15:37:56,980\tINFO streaming_executor.py:93 -- Executing DAG InputDataBuffer[Input] -> TaskPoolMapOperator[FlatMap(extract_sections->Limit[1])] -> LimitOperator[limit=1]\n",
      "2024-04-23 15:37:56,981\tINFO streaming_executor.py:94 -- Execution config: ExecutionOptions(resource_limits=ExecutionResources(cpu=None, gpu=None, object_store_memory=None), locality_with_output=False, preserve_order=False, actor_locality_enabled=True, verbose_progress=False)\n",
      "2024-04-23 15:37:56,981\tINFO streaming_executor.py:96 -- Tip: For detailed progress reporting, run `ray.data.DataContext.get_current().execution_options.verbose_progress = True`\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Running 0:   0%|          | 0/1 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "page_content='Ray Dashboard#\\nRay provides a web-based dashboard for monitoring and debugging Ray applications.\\nThe visual representation of the system state, allows users to track the performance\\nof applications and troubleshoot issues.' metadata={'source': 'https://docs.ray.io/en/master/ray-observability/getting-started.html#ray-dashboard'}\n"
     ]
    }
   ],
   "source": [
    "# Chunk a sample section\n",
    "sample_section = sections_ds.take(1)[0]\n",
    "chunks = text_splitter.create_documents(\n",
    "    texts=[sample_section[\"text\"]], \n",
    "    metadatas=[{\"source\": sample_section[\"source\"]}])\n",
    "print (chunks[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d38775f-0a4b-4aad-866d-1a3231b32df7",
   "metadata": {},
   "source": [
    "While chunking our dataset is relatively fast, let’s wrap the chunking logic into a function so that we can apply the workload at scale so that chunking remains just as fast as our data sources grow:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c15a38d-066b-4623-8448-fdd0ac5ddf4d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def chunk_section(section, chunk_size, chunk_overlap):\n",
    "    text_splitter = RecursiveCharacterTextSplitter(\n",
    "        separators=[\"\\n\\n\", \"\\n\", \" \", \"\"],\n",
    "        chunk_size=chunk_size,\n",
    "        chunk_overlap=chunk_overlap,\n",
    "        length_function=len)\n",
    "    chunks = text_splitter.create_documents(\n",
    "        texts=[section[\"text\"]], \n",
    "        metadatas=[{\"source\": section[\"source\"]}])\n",
    "    return [{\"text\": chunk.page_content, \"source\": chunk.metadata[\"source\"]} for chunk in chunks]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5be23c62-3108-49be-994d-5aa033162c71",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-04-23 15:37:57,386\tINFO streaming_executor.py:93 -- Executing DAG InputDataBuffer[Input] -> TaskPoolMapOperator[FlatMap(extract_sections)->FlatMap(partial)]\n",
      "2024-04-23 15:37:57,387\tINFO streaming_executor.py:94 -- Execution config: ExecutionOptions(resource_limits=ExecutionResources(cpu=None, gpu=None, object_store_memory=None), locality_with_output=False, preserve_order=False, actor_locality_enabled=True, verbose_progress=False)\n",
      "2024-04-23 15:37:57,387\tINFO streaming_executor.py:96 -- Tip: For detailed progress reporting, run `ray.data.DataContext.get_current().execution_options.verbose_progress = True`\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Running 0:   0%|          | 0/200 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-04-23 15:39:15,841\tINFO streaming_executor.py:93 -- Executing DAG InputDataBuffer[Input] -> TaskPoolMapOperator[FlatMap(extract_sections)->FlatMap(partial->Limit[1])] -> LimitOperator[limit=1]\n",
      "2024-04-23 15:39:15,841\tINFO streaming_executor.py:94 -- Execution config: ExecutionOptions(resource_limits=ExecutionResources(cpu=None, gpu=None, object_store_memory=None), locality_with_output=False, preserve_order=False, actor_locality_enabled=True, verbose_progress=False)\n",
      "2024-04-23 15:39:15,842\tINFO streaming_executor.py:96 -- Tip: For detailed progress reporting, run `ray.data.DataContext.get_current().execution_options.verbose_progress = True`\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "30310 chunks\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Running 0:   0%|          | 0/1 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'text': 'State API#\\nNote\\nAPIs are alpha. This feature requires a full installation of Ray using pip install \"ray[default]\".\\nFor an overview with examples see Monitoring Ray States.\\nFor the CLI reference see Ray State CLI Reference or Ray Log CLI Reference.', 'source': 'https://docs.ray.io/en/master/ray-observability/reference/api.html#state-api'}\n"
     ]
    }
   ],
   "source": [
    "# Scale chunking\n",
    "chunks_ds = sections_ds.flat_map(partial(\n",
    "    chunk_section, \n",
    "    chunk_size=chunk_size, \n",
    "    chunk_overlap=chunk_overlap))\n",
    "print(f\"{chunks_ds.count()} chunks\")\n",
    "chunks_ds.show(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9c23b31-e7b3-4078-abf7-683f448f5b19",
   "metadata": {},
   "source": [
    "## Embed data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb44e2b4-d48a-4f27-b9e4-99579917b18e",
   "metadata": {},
   "source": [
    "Now that we've created small chunks from our sections, we need a way to identify the most relevant ones for a given query. A very effective and quick method is to embed our data using a pretrained model and use the same model to embed the query. We can then compute the distance between all of the chunk embeddings and our query embedding to determine the top-k chunks. There are many different pretrained models to choose from to embed our data but the most popular ones can be discovered through [HuggingFace's Massive Text Embedding Benchmark (MTEB)](https://huggingface.co/spaces/mteb/leaderboard) leaderboard. These models were pretrained on very large text corpus through tasks such as next/masked token prediction which allowed them to learn to represent subtokens in N dimensions and capture semantic relationships. We can leverage this to represent our data and identify the most relevant contexts to use to answer a given query. We're using Langchain's Embedding wrappers ([HuggingFaceEmbeddings](https://api.python.langchain.com/en/latest/embeddings/langchain.embeddings.huggingface.HuggingFaceEmbeddings.html) and [OpenAIEmbeddings](https://api.python.langchain.com/en/latest/embeddings/langchain.embeddings.openai.OpenAIEmbeddings.html)) to easily load the models and embed our document chunks.\n",
    "\n",
    "**Note**: embeddings aren't the only way to determine the more relevant chunks. We could also use an LLM to decide! However, because LLMs are much larger than these embedding models and have maximum context lengths, it's better to use embeddings to retrieve the top k chunks. And then we could use LLMs on the fewer k chunks to determine the <k chunks to use as the context to answer our query. We could also use reranking (ex. [Cohere Rerank](https://txt.cohere.com/rerank/)) to further identify the most relevant chunks to use. We could also combine embeddings with traditional information retrieval methods such as keyword matching, which could be useful for matching for unique tokens that may potentially be lost when embedding subtokens."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "712fe08b-fd19-4cb8-94d9-a7570b2dc09d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain.embeddings import OpenAIEmbeddings\n",
    "from langchain.embeddings.huggingface import HuggingFaceEmbeddings\n",
    "import numpy as np\n",
    "from ray.data import ActorPoolStrategy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "290c066a-100b-4f09-98ba-e79a93cd4302",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def get_embedding_model(embedding_model_name, model_kwargs, encode_kwargs):\n",
    "    if embedding_model_name == \"text-embedding-ada-002\":\n",
    "        embedding_model = OpenAIEmbeddings(\n",
    "            model=embedding_model_name,\n",
    "            openai_api_base=os.environ[\"OPENAI_API_BASE\"],\n",
    "            openai_api_key=os.environ[\"OPENAI_API_KEY\"])\n",
    "    else:\n",
    "        embedding_model = HuggingFaceEmbeddings(\n",
    "            model_name=embedding_model_name,  # also works with model_path\n",
    "            model_kwargs=model_kwargs,\n",
    "            encode_kwargs=encode_kwargs)\n",
    "    return embedding_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83b6a5a3-cd2d-4987-838a-be13e9553080",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "class EmbedChunks:\n",
    "    def __init__(self, model_name):\n",
    "        self.embedding_model = get_embedding_model(\n",
    "            embedding_model_name=model_name,\n",
    "            model_kwargs={\"device\": \"cuda\"},\n",
    "            encode_kwargs={\"device\": \"cuda\", \"batch_size\": 100})\n",
    "    def __call__(self, batch):\n",
    "        embeddings = self.embedding_model.embed_documents(batch[\"text\"])\n",
    "        return {\"text\": batch[\"text\"], \"source\": batch[\"source\"], \"embeddings\": embeddings}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e39c99a-5f33-4052-ba0e-cbc69d104ec1",
   "metadata": {},
   "source": [
    "Here we're able to embed our chunks at scale by using [map_batches](https://docs.ray.io/en/latest/data/api/doc/ray.data.Dataset.map_batches.html). All we had to do was define the `batch_size` and the compute resources."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9715a01e-dc67-4342-a0cb-30e770852097",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Embed chunks\n",
    "embedding_model_name = \"thenlper/gte-base\"\n",
    "embedded_chunks = chunks_ds.map_batches(\n",
    "    EmbedChunks,\n",
    "    fn_constructor_kwargs={\"model_name\": embedding_model_name},\n",
    "    batch_size=100, \n",
    "    num_gpus=1,\n",
    "    compute=ActorPoolStrategy(size=1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67dffa1f-19a3-4411-af3f-b161a47ee164",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2023-12-27 18:56:56,384\tINFO streaming_executor.py:93 -- Executing DAG InputDataBuffer[Input] -> TaskPoolMapOperator[FlatMap(extract_sections)->FlatMap(partial)] -> ActorPoolMapOperator[MapBatches(EmbedChunks->Limit[1])] -> LimitOperator[limit=1]\n",
      "2023-12-27 18:56:56,385\tINFO streaming_executor.py:94 -- Execution config: ExecutionOptions(resource_limits=ExecutionResources(cpu=None, gpu=None, object_store_memory=None), locality_with_output=False, preserve_order=False, actor_locality_enabled=True, verbose_progress=False)\n",
      "2023-12-27 18:56:56,385\tINFO streaming_executor.py:96 -- Tip: For detailed progress reporting, run `ray.data.DataContext.get_current().execution_options.verbose_progress = True`\n",
      "2023-12-27 18:56:56,401\tINFO actor_pool_map_operator.py:106 -- MapBatches(EmbedChunks->Limit[1]): Waiting for 1 pool actors to start...\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Running 0:   0%|          | 0/1 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "embedding size: 1024\n",
      "Reference#\n",
      "Monitor and debug your Ray applications and clusters using the API and CLI documented in these references.\n",
      "The guides include:\n",
      "State API\n",
      "State CLI\n",
      "System Metrics\n"
     ]
    }
   ],
   "source": [
    "# Sample\n",
    "sample = embedded_chunks.take(1)\n",
    "print (\"embedding size:\", len(sample[0][\"embeddings\"]))\n",
    "print (sample[0][\"text\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09187588-e1dc-44c5-b88b-cc8ebe3f9c48",
   "metadata": {},
   "source": [
    "## Index data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb9f53f1-2720-4a47-8b2f-1b206c38c425",
   "metadata": {},
   "source": [
    "Now that we have our embedded chunks, we need to index (store) them somewhere so that we can retrieve them quickly for inference. While there are many popular vector database options, we're going to use [Postgres with pgvector](https://github.com/pgvector/pgvector) for it's simplificty and performance. We'll create a table (`document`) and write the (`text`, `source`, `embedding`) triplets for each embedded chunk we have.\n",
    "\n",
    "<img width=\"700\" src=\"https://images.ctfassets.net/xjan103pcp94/3z1ryYkOtUjj6N1IuavJPf/ae60dc4a10c94e2cc928c38701befb51/image2.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb6bf17c-da04-4bd8-af2f-dc79e4e27913",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import psycopg\n",
    "from pgvector.psycopg import register_vector\n",
    "os.environ[\"MIGRATION_FP\"] = f\"../migrations/vector-{EMBEDDING_DIMENSIONS[embedding_model_name]}.sql\"\n",
    "os.environ[\"SQL_DUMP_FP\"] = f\"{EFS_DIR}/sql_dumps/{embedding_model_name.split('/')[-1]}_{chunk_size}_{chunk_overlap}.sql\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "633f3c88-0c88-48b5-a6f9-4b08e3a0dc43",
   "metadata": {},
   "source": [
    "**Note**: Run `bash setup-pgvector.sh` first!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b64894f-4de1-42b8-b56f-37800b5ce591",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "psql: error: connection to server at \"localhost\" (127.0.0.1), port 5432 failed: Connection refused\n",
      "\tIs the server running on that host and accepting TCP/IP connections?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "../migrations/vector-768.sql\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "psql: error: connection to server on socket \"/var/run/postgresql/.s.PGSQL.5432\" failed: No such file or directory\n",
      "\tIs the server running locally and accepting connections on that socket?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/mnt/shared_storage/ray-assistant-data/sql_dumps/gte-base_300_50.sql\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "# Set up\n",
    "psql \"$DB_CONNECTION_STRING\" -c \"DROP TABLE IF EXISTS document;\"\n",
    "echo $MIGRATION_FP\n",
    "sudo -u postgres psql -f $MIGRATION_FP\n",
    "echo $SQL_DUMP_FP"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "806b47aa-f3c1-44d3-b041-a3a4f0867653",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%bash\n",
    "# Drop the existing `document` table and create a new one with the schema to store embeddings. \n",
    "psql \"$DB_CONNECTION_STRING\" -c \"DROP TABLE IF EXISTS document;\"  # drop\n",
    "sudo -u postgres psql -f $MIGRATION_FP  # create\n",
    "psql \"$DB_CONNECTION_STRING\" -c \"SELECT count(*) FROM document;\"  # num rows\n",
    "\n",
    "# DROP TABLE\n",
    "# CREATE TABLE\n",
    "#  count \n",
    "# -------\n",
    "#      0\n",
    "# (1 row)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fe36601-1269-482b-9bac-b52b32fb338e",
   "metadata": {},
   "source": [
    "If we don't have an index saved already, we can index the data and save it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21480e47-4a17-49d8-b4e7-c301e8040a69",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "class StoreResults:\n",
    "    def __call__(self, batch):\n",
    "        with psycopg.connect(os.environ[\"DB_CONNECTION_STRING\"]) as conn:\n",
    "            register_vector(conn)\n",
    "            with conn.cursor() as cur:\n",
    "                for text, source, embedding in zip(batch[\"text\"], batch[\"source\"], batch[\"embeddings\"]):\n",
    "                    cur.execute(\"INSERT INTO document (text, source, embedding) VALUES (%s, %s, %s)\", (text, source, embedding,),)\n",
    "        return {}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d4f119d-535d-4548-bb83-177e3bcefeed",
   "metadata": {},
   "source": [
    "And once again, we can use Ray Data’s [map_batches](https://docs.ray.io/en/latest/data/api/doc/ray.data.Dataset.map_batches.html) to perform this indexing in parallel:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c4582d9-40ba-4a94-81ac-259b3851f837",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2023-12-27 18:57:35,648\tINFO streaming_executor.py:93 -- Executing DAG InputDataBuffer[Input] -> TaskPoolMapOperator[FlatMap(extract_sections)->FlatMap(partial)] -> ActorPoolMapOperator[MapBatches(EmbedChunks)] -> ActorPoolMapOperator[MapBatches(StoreResults)]\n",
      "2023-12-27 18:57:35,648\tINFO streaming_executor.py:94 -- Execution config: ExecutionOptions(resource_limits=ExecutionResources(cpu=None, gpu=None, object_store_memory=None), locality_with_output=False, preserve_order=False, actor_locality_enabled=True, verbose_progress=False)\n",
      "2023-12-27 18:57:35,649\tINFO streaming_executor.py:96 -- Tip: For detailed progress reporting, run `ray.data.DataContext.get_current().execution_options.verbose_progress = True`\n",
      "2023-12-27 18:57:35,664\tINFO actor_pool_map_operator.py:106 -- MapBatches(EmbedChunks): Waiting for 1 pool actors to start...\n",
      "2023-12-27 18:57:42,073\tINFO actor_pool_map_operator.py:106 -- MapBatches(StoreResults): Waiting for 6 pool actors to start...\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6c8a60a6a0684709b2fe3ac7b81ebf9a",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Running 0:   0%|          | 0/200 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Index data\n",
    "embedded_chunks.map_batches(\n",
    "    StoreResults,\n",
    "    batch_size=128,\n",
    "    num_cpus=1,\n",
    "    compute=ActorPoolStrategy(size=6),\n",
    ").materialize()\n",
    "\n",
    "# Verify whether the embedding was stored successfully in Postgres or not.\n",
    "# sudo -u postgres psql\n",
    "# SELECT * FROM document LIMIT 10;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40925cd6-41e8-4651-9692-aeb399b68af6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%bash\n",
    "# Save index\n",
    "rm -rf $SQL_DUMP_FP\n",
    "mkdir -p $(dirname \"$SQL_DUMP_FP\") && touch $SQL_DUMP_FP\n",
    "sudo -u postgres pg_dump -c > $SQL_DUMP_FP  # save"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "318798c3-d119-4eb5-ad81-b2834516151a",
   "metadata": {},
   "source": [
    "# Retrieval"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "974cc146-c337-4478-a119-5daadedd340c",
   "metadata": {},
   "source": [
    "With our embedded chunks indexed in our vector database, we're ready to perform retrieval for a given query. We'll start by using the same embedding model we used to embed our text chunks to now embed the incoming query.\n",
    "\n",
    "<img width=\"1000\" src=\"https://images.ctfassets.net/xjan103pcp94/1hKBrFU2lyR5LLebFyq2ZL/8845c36ff98eb47005338de6ab6dbf50/image14.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "480d4c49-5870-471e-a617-86f7d3fa13d0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import json\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39c3f410-89a2-4992-8cd1-63aca5bf9936",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "e4980860fc4e4b33b6a4d2023da47571",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading .gitattributes:   0%|          | 0.00/1.52k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6f6efbd4d4f44897b9d0afa4221e98ba",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading 1_Pooling/config.json:   0%|          | 0.00/190 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "bb527f71a7074b8a959871bf6af3234c",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading README.md:   0%|          | 0.00/68.1k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ba6ab30492bc418db4a8c0c0e94b1807",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading config.json:   0%|          | 0.00/618 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1c69f83aa7244e7a82c373239579bcd8",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading model.safetensors:   0%|          | 0.00/219M [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "587dbb9e4596404eb4ea754d14ccf409",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading onnx/config.json:   0%|          | 0.00/630 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "54c33bf01b874262bf676416647b8790",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading model.onnx:   0%|          | 0.00/436M [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "79ffe43a2e0e4fa68acae617d41c2ad6",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)cial_tokens_map.json:   0%|          | 0.00/125 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0bc1532dc584492bbfd21ec03cdfa9b9",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading onnx/tokenizer.json:   0%|          | 0.00/712k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0ee54809d1bc4740a573b52c80b53337",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)okenizer_config.json:   0%|          | 0.00/314 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f0fe9fcc9c114b5eb0e93a5425057e7d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading onnx/vocab.txt:   0%|          | 0.00/232k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f666e1d6c9434e7e85dcb944cc484412",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading pytorch_model.bin:   0%|          | 0.00/219M [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3e3e1f8cc218404f9bc7f12a1bc87a90",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)nce_bert_config.json:   0%|          | 0.00/57.0 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0ff2417687d5418497a0e9f431e25c3e",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)cial_tokens_map.json:   0%|          | 0.00/125 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f7dc16c9f10d425abb153bdfccab229f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading tokenizer.json:   0%|          | 0.00/712k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3c4bfb3f00354be8a9ebbb2da3246cb5",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading tokenizer_config.json:   0%|          | 0.00/314 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "38ae029cea3f42ebadc61dde75694d08",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading vocab.txt:   0%|          | 0.00/232k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8a05efb7db1646649629a846092e4f8d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading modules.json:   0%|          | 0.00/385 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "768"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Embed query\n",
    "embedding_model = HuggingFaceEmbeddings(model_name=embedding_model_name)\n",
    "query = \"What is the default batch size for map_batches?\"\n",
    "embedding = np.array(embedding_model.embed_query(query))\n",
    "len(embedding)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd3d604e-437c-4917-9503-cdc6a74df982",
   "metadata": {},
   "source": [
    "Then, we'll retrieve the top most revelant chunks by extracting the closest embedded chunks to our embedded query. We use cosine distance (`<=>`) but there are [many options](https://github.com/pgvector/pgvector#vector-operators) to choose from. Once we retrieve the top `num_chunks`, we can collect the text for each chunk and use it as context to generate a response."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "073a796f-e0e7-46d3-8151-01f47befec4c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Get context\n",
    "num_chunks = 5\n",
    "with psycopg.connect(os.environ[\"DB_CONNECTION_STRING\"]) as conn:\n",
    "    register_vector(conn)\n",
    "    with conn.cursor() as cur:\n",
    "        # cur.execute(\"SELECT * FROM document ORDER BY embedding <=> %s LIMIT %s\", (embedding, num_chunks))\n",
    "        cur.execute(\"SELECT *, (embedding <=> %s) AS similarity_score FROM document ORDER BY similarity_score LIMIT %s\", (embedding, num_chunks))\n",
    "        rows = cur.fetchall()\n",
    "        ids = [row[0] for row in rows]\n",
    "        context = [{\"text\": row[1]} for row in rows]\n",
    "        sources = [row[2] for row in rows]\n",
    "        scores = [row[4] for row in rows]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a3cfb58-4c25-4a14-9634-f6a24fac970b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "14887\n",
      "0.06207154583656549\n",
      "https://docs.ray.io/en/master/data/api/doc/ray.data.Dataset.map_batches.html#ray-data-dataset-map-batches\n",
      "entire blocks as batches (blocks may contain different numbers of rows).\n",
      "The actual size of the batch provided to fn may be smaller than\n",
      "batch_size if batch_size doesn’t evenly divide the block(s) sent\n",
      "to a given map task. Default batch_size is 4096 with “default”.\n",
      "\n",
      "15009\n",
      "0.06654224236622186\n",
      "https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-size\n",
      "batch_size.\n",
      "Note\n",
      "The default batch size depends on your resource type. If you’re using CPUs,\n",
      "the default batch size is 4096. If you’re using GPUs, you must specify an explicit\n",
      "batch size.\n",
      "\n",
      "15051\n",
      "0.07562640027010847\n",
      "https://docs.ray.io/en/master/data/batch_inference.html#configuring-batch-size\n",
      "# Specify that each input batch should be of size 2.\n",
      "ds.map_batches(assert_batch, batch_size=2)\n",
      "Caution\n",
      "The default batch_size of 4096 may be too large for datasets with large rows\n",
      "(for example, tables with many columns or a collection of large images).\n",
      "\n",
      "15035\n",
      "0.08958362418550658\n",
      "https://docs.ray.io/en/master/data/batch_inference.html#configuring-batch-size\n",
      "Configuring Batch Size#\n",
      "Configure the size of the input batch that’s passed to __call__ by setting the batch_size argument for ds.map_batches()\n",
      "\n",
      "2283\n",
      "0.09523273435425617\n",
      "https://docs.ray.io/en/master/tune/getting-started.html#setting-up-a-tuner-for-a-training-run-with-tune\n",
      "batch_size=64,\n",
      "        shuffle=True)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for i, item in enumerate(context):\n",
    "    print (ids[i])\n",
    "    print (scores[i])\n",
    "    print (sources[i])\n",
    "    print (item[\"text\"])\n",
    "    print ()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fc7d446-3936-4c4a-aa01-f6dc46991416",
   "metadata": {},
   "source": [
    "Let's wrap this into a convenient function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d26a88bd-fbf0-4023-bfc7-e0501073caeb",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def semantic_search(query, embedding_model, k):\n",
    "    embedding = np.array(embedding_model.embed_query(query))\n",
    "    with psycopg.connect(os.environ[\"DB_CONNECTION_STRING\"]) as conn:\n",
    "        register_vector(conn)\n",
    "        with conn.cursor() as cur:\n",
    "            cur.execute(\"SELECT * FROM document ORDER BY embedding <=> %s LIMIT %s\", (embedding, k),)\n",
    "            rows = cur.fetchall()\n",
    "            semantic_context = [{\"id\": row[0], \"text\": row[1], \"source\": row[2]} for row in rows]\n",
    "    return semantic_context"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f5738d23-91e3-4016-826e-716872f76b62",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Generation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bec2b7c-35be-40d1-9e12-29d7d6178d4d",
   "metadata": {},
   "source": [
    "We can now use the context to generate a response from our LLM. Without this relevant context that we retrieved, the LLM may not have been able to accurately answer our question. And as our data grows, we can just as easily embed and index any new data and be able to retrieve it to answer questions.\n",
    "\n",
    "<img width=\"500\" src=\"https://images.ctfassets.net/xjan103pcp94/38I8en8Tyf0cM4LUhjygoq/739d456c80841b4c28fe80f73ea5856b/image16.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b55cc1d7-e110-4d9d-abc4-36576db25f92",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import openai\n",
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6510f9dc-5870-4c9a-8ea0-7661d1914f8a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "d930a41ee53f48fdb23caa48bc8883fa",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading tokenizer_config.json:   0%|          | 0.00/28.0 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8b31a4cdb53843e7a57bf22267ff8b6f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading vocab.txt:   0%|          | 0.00/232k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "acfa0c58588243af979c6af1a33b20ea",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading tokenizer.json:   0%|          | 0.00/466k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "3f9c1e847d6e48ce8e98e75db76328ff",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading config.json:   0%|          | 0.00/570 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from rag.generate import prepare_response\n",
    "from rag.utils import get_client"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "697e65e8-4d69-4870-9f09-0e128008b94e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def generate_response(\n",
    "    llm, temperature=0.0, stream=True,\n",
    "    system_content=\"\", assistant_content=\"\", user_content=\"\", \n",
    "    max_retries=1, retry_interval=60):\n",
    "    \"\"\"Generate response from an LLM.\"\"\"\n",
    "    retry_count = 0\n",
    "    client = get_client(llm=llm)\n",
    "    messages = [{\"role\": role, \"content\": content} for role, content in [\n",
    "        (\"system\", system_content), \n",
    "        (\"assistant\", assistant_content), \n",
    "        (\"user\", user_content)] if content]\n",
    "    while retry_count <= max_retries:\n",
    "        try:\n",
    "            chat_completion = client.chat.completions.create(\n",
    "                model=llm,\n",
    "                temperature=temperature,\n",
    "                stream=stream,\n",
    "                messages=messages,\n",
    "            )\n",
    "            return prepare_response(chat_completion, stream=stream)\n",
    "\n",
    "        except Exception as e:\n",
    "            print(f\"Exception: {e}\")\n",
    "            time.sleep(retry_interval)  # default is per-minute rate limits\n",
    "            retry_count += 1\n",
    "    return \"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dfe6bd9-bad1-4b21-9aea-fc2e127ba3fc",
   "metadata": {},
   "source": [
    "**Note**: We’re using a temperature of 0.0 to enable reproducible experiments but you should adjust this based on your use case. For use cases that need to always be factually grounded, we recommend very low temperature values while more creative tasks can benefit from higher temperatures."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7864459b-8bb3-469b-9583-ae6fa527b124",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['entire blocks as batches (blocks may contain different numbers of rows).\\nThe actual size of the batch provided to fn may be smaller than\\nbatch_size if batch_size doesn’t evenly divide the block(s) sent\\nto a given map task. Default batch_size is 4096 with “default”.', 'batch_size.\\nNote\\nThe default batch size depends on your resource type. If you’re using CPUs,\\nthe default batch size is 4096. If you’re using GPUs, you must specify an explicit\\nbatch size.', '# Specify that each input batch should be of size 2.\\nds.map_batches(assert_batch, batch_size=2)\\nCaution\\nThe default batch_size of 4096 may be too large for datasets with large rows\\n(for example, tables with many columns or a collection of large images).', 'Configuring Batch Size#\\nConfigure the size of the input batch that’s passed to __call__ by setting the batch_size argument for ds.map_batches()', 'batch_size=64,\\n        shuffle=True)']\n"
     ]
    }
   ],
   "source": [
    "context_results = semantic_search(query=query, embedding_model=embedding_model, k=5)\n",
    "context = [item[\"text\"] for item in context_results]\n",
    "print(context)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9de3685b-6839-445f-9baa-68a5e863562a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  The default batch size for map_batches is 4096."
     ]
    }
   ],
   "source": [
    "# Generate response\n",
    "query = \"What is the default batch size for map_batches?\"\n",
    "response = generate_response(\n",
    "    llm=\"meta-llama/Llama-2-70b-chat-hf\",\n",
    "    temperature=0.0,\n",
    "    stream=True,\n",
    "    system_content=\"Answer the query using the context provided. Be succinct.\",\n",
    "    user_content=f\"query: {query}, context: {context}\")\n",
    "# Stream response\n",
    "for content in response:\n",
    "    print(content, end='', flush=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77e0511e-d79c-4d54-a50f-163c11fc8643",
   "metadata": {},
   "source": [
    "## Agent"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bad51191-19d1-40d5-bbc8-72245fafd154",
   "metadata": {},
   "source": [
    "Let's combine the context retrieval and response generation together into a convenient query agent that we can use to easily generate our responses. This will take care of setting up our agent (embedding and LLM model), as well as the context retrieval, and pass it to our LLM for response generation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc002554-a525-48e0-8389-e60594d29221",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from rag.embed import get_embedding_model\n",
    "from rag.utils import get_num_tokens, trim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffcc892a-ceee-487a-aecf-db116f53e89f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "class QueryAgent:\n",
    "    def __init__(self, embedding_model_name=\"thenlper/gte-base\",\n",
    "                 llm=\"meta-llama/Llama-2-70b-chat-hf\", temperature=0.0, \n",
    "                 max_context_length=4096, system_content=\"\", assistant_content=\"\"):\n",
    "        \n",
    "        # Embedding model\n",
    "        self.embedding_model = get_embedding_model(\n",
    "            embedding_model_name=embedding_model_name, \n",
    "            model_kwargs={\"device\": \"cuda\"}, \n",
    "            encode_kwargs={\"device\": \"cuda\", \"batch_size\": 100})\n",
    "        \n",
    "        # Context length (restrict input length to 50% of total context length)\n",
    "        max_context_length = int(0.5*max_context_length)\n",
    "        \n",
    "        # LLM\n",
    "        self.llm = llm\n",
    "        self.temperature = temperature\n",
    "        self.context_length = max_context_length - get_num_tokens(system_content + assistant_content)\n",
    "        self.system_content = system_content\n",
    "        self.assistant_content = assistant_content\n",
    "\n",
    "    def __call__(self, query, num_chunks=5, stream=True):\n",
    "        # Get sources and context\n",
    "        context_results = semantic_search(\n",
    "            query=query, \n",
    "            embedding_model=self.embedding_model, \n",
    "            k=num_chunks)\n",
    "            \n",
    "        # Generate response\n",
    "        context = [item[\"text\"] for item in context_results]\n",
    "        sources = [item[\"source\"] for item in context_results]\n",
    "        user_content = f\"query: {query}, context: {context}\"\n",
    "        answer = generate_response(\n",
    "            llm=self.llm,\n",
    "            temperature=self.temperature,\n",
    "            stream=stream,\n",
    "            system_content=self.system_content,\n",
    "            assistant_content=self.assistant_content,\n",
    "            user_content=trim(user_content, self.context_length))\n",
    "\n",
    "        # Result\n",
    "        result = {\n",
    "            \"question\": query,\n",
    "            \"sources\": sources,\n",
    "            \"answer\": answer,\n",
    "            \"llm\": self.llm,\n",
    "        }\n",
    "        return result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29fc2167-3f0b-4cac-96a0-67ecb9854f2c",
   "metadata": {},
   "source": [
    "With this, we can use our RAG application in just a few lines:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6e0125b-cc86-4811-aded-e77b661e068a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "embedding_model_name = \"thenlper/gte-base\"\n",
    "llm = \"meta-llama/Llama-2-7b-chat-hf\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ad3b1224-922b-40b5-9979-9075c6ef100e",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"question\": \"What is the default batch size for map_batches?\",\n",
      "  \"sources\": [\n",
      "    \"https://docs.ray.io/en/master/data/api/doc/ray.data.Dataset.map_batches.html#ray-data-dataset-map-batches\",\n",
      "    \"https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-size\",\n",
      "    \"https://docs.ray.io/en/master/data/batch_inference.html#configuring-batch-size\",\n",
      "    \"https://docs.ray.io/en/master/data/batch_inference.html#configuring-batch-size\",\n",
      "    \"https://docs.ray.io/en/master/tune/getting-started.html#setting-up-a-tuner-for-a-training-run-with-tune\"\n",
      "  ],\n",
      "  \"answer\": \"  The default batch size for `map_batches` is 4096. However, this can vary depending on the resource type, with a default of 4096 for CPUs and a specific batch size required for GPUs. It's important to note that the default batch size may be too large for datasets with large rows, and configuring the batch size can help improve performance.\",\n",
      "  \"llm\": \"meta-llama/Llama-2-7b-chat-hf\"\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "query = \"What is the default batch size for map_batches?\"\n",
    "system_content = \"Answer the query using the context provided. Be succinct.\"\n",
    "agent = QueryAgent(\n",
    "    embedding_model_name=embedding_model_name,\n",
    "    llm=llm,\n",
    "    max_context_length=MAX_CONTEXT_LENGTHS[llm],\n",
    "    system_content=system_content)\n",
    "result = agent(query=query, stream=False)\n",
    "print(json.dumps(result, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34feb8ca-5e72-4b12-8ab3-55ca5af05109",
   "metadata": {},
   "source": [
    "# Evaluation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6676dbb2-a143-463c-8de2-9a2763e12059",
   "metadata": {},
   "source": [
    "So far, we've chosen typical/arbitrary values for the various parts of our RAG application. But if we were to change something, such as our chunking logic, embedding model, LLM, etc. how can we know that we have a better configuration than before? A generative task like this is very difficult to quantitatively assess and so we need to develop reliable ways to do so.\n",
    "\n",
    "Because we have many moving parts in our application, we need to perform both unit/component and end-to-end evaluation. Component-wise evaluation can involve evaluating our retrieval in isolation (is the best source in our set of retrieved chunks) and evaluating our LLMs response (given the best source, is the LLM able to produce a quality answer). And for end-to-end evaluation, we can assess the quality of the entire system (given the data sources, what is the quality of the response).\n",
    "We'll be asking our evaluator LLM to score the quality of the response between 1-5 using the context, however, we could also have it produce scores for other dimensions such as hallucination (is the generated answer using information only from the provided context), toxicity, etc.\n",
    "\n",
    "**Note**: We could have constrained the score to be binary (0/1), which might be more interpretable (ex. the response was either correct or incorrect). However, we introduced a higher variance in our scores to develop a deeper, fine-grained, understanding of how LLMs score responses (ex. LLM bias towards responses).\n",
    "\n",
    "\n",
    "\n",
    "<img width=\"1000\" src=\"https://images.ctfassets.net/xjan103pcp94/17UQdsEImsXOOdDlT06bvi/4a9b9e46e157541a1178b6938624176a/llm_evaluations.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c6f7b9e-4b77-4b0c-8660-ba8047356415",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# If running tests / small samples, set num_samples to <10\n",
    "EXPERIMENTS_DIR = Path(ROOT_DIR, \"experiments\")\n",
    "NUM_SAMPLES = None  # None = all samples"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fa7a068-fc77-4928-bf58-52321616f9d5",
   "metadata": {},
   "source": [
    "## Evaluator"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a19d3e8-b7e8-4d69-aa70-118af1fb1708",
   "metadata": {},
   "source": [
    "We're going to start by determining our evaluator. Given a response to a query and relevant context, our evaluator should be a trusted way to score/assess the quality of the response. But before we can determine our evaluator, we need a dataset of questions and the source where the answer comes from. We can use this dataset to ask our different evaluators to provide an answer and then rate their answer (ex. score between 1-5). We can then inspect this dataset to determine if our evaluator is unbiased and has sound reasoning for the scores that are assigned.\n",
    "\n",
    "**Note**: We’re evaluating the ability of our LLM to generate a response given the relevant context. This is a component-level evaluation (`quality_score (LLM)`) because we aren’t using retrieval to fetch the relevant context."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03acb7e0-7bcb-4e2f-8552-5fe182a278db",
   "metadata": {},
   "source": [
    "We'll start by manually creating our dataset (keep reading if you can’t manually create a dataset). We have a list of user queries and the ideal source to answer the query [datasets/eval-dataset-v1.jsonl](https://github.com/ray-project/llm-applications/blob/main/datasets/eval-dataset-v1.jsonl). We will our LLM app above to generate reference answer for each query/source pair using `gpt-4`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa6cab00-d68d-4745-a735-048d72d8e5d6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from bs4 import BeautifulSoup\n",
    "from IPython.display import JSON, clear_output, display\n",
    "from tqdm import tqdm\n",
    "import urllib.parse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c58d5dd-bd0f-40eb-99e3-ea305a925127",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from rag.evaluate import extract_from_response\n",
    "from rag.data import fetch_text"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca019cc9-3241-4453-b63d-ef7c44ba584a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Load dataset\n",
    "with open(Path(ROOT_DIR, \"datasets/eval-dataset-v1.jsonl\"), \"r\") as f:\n",
    "    data = [json.loads(item) for item in list(f)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3cd9e509-1fe6-42ad-886f-0e118e8dd35d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'question': 'I’m struggling a bit with Ray Data type conversions when I do map_batches. Any advice?',\n",
       "  'source': 'https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-format'},\n",
       " {'question': 'How does autoscaling work in a Ray Serve application?',\n",
       "  'source': 'https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling'},\n",
       " {'question': 'how do I get the address of a ray node',\n",
       "  'source': 'https://docs.ray.io/en/master/ray-core/miscellaneous.html#node-information'},\n",
       " {'question': 'Does Ray support NCCL?',\n",
       "  'source': 'https://docs.ray.io/en/master/ray-more-libs/ray-collective.html'},\n",
       " {'question': 'Is Ray integrated with DeepSpeed?',\n",
       "  'source': 'https://docs.ray.io/en/master/ray-air/examples/gptj_deepspeed_fine_tuning.html#fine-tuning-the-model-with-ray-air-a-name-train-a'}]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eaeb8f67-0fef-4b0e-a362-cceb04f3295b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\nConfiguring batch format#\\nRay Data represents batches as dicts of NumPy ndarrays or pandas DataFrames. By\\ndefault, Ray Data represents batches as dicts of NumPy ndarrays.\\nTo configure the batch type, specify batch_format in\\nmap_batches(). You can return either format from your function.\\n\\n\\n\\nNumPy\\nfrom typing import Dict\\nimport numpy as np\\nimport ray\\n\\ndef increase_brightness(batch: Dict[str, np.ndarray]) -> Dict[str, np.ndarray]:\\n    batch[\"image\"] = np.clip(batch[\"image\"] + 4, 0, 255)\\n    return batch\\n\\nds = (\\n    ray.data.read_images(\"s3://anonymous@ray-example-data/image-datasets/simple\")\\n    .map_batches(increase_brightness, batch_format=\"numpy\")\\n)\\n\\n\\n\\n\\n\\npandas\\nimport pandas as pd\\nimport ray\\n\\ndef drop_nas(batch: pd.DataFrame) -> pd.DataFrame:\\n    return batch.dropna()\\n\\nds = (\\n    ray.data.read_csv(\"s3://anonymous@air-example-data/iris.csv\")\\n    .map_batches(drop_nas, batch_format=\"pandas\")\\n)\\n\\n\\n\\n\\n'"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Sample\n",
    "uri = \"https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-format\"\n",
    "fetch_text(uri=uri)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dec3460f-c07a-4a2f-95f7-d1ef85d1a064",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Content for inference\n",
    "system_content = \"\"\"\n",
    "    Answer the query using the context provided. Be succinct.\n",
    "    Then, you must {score} your response between 1 and 5.\n",
    "    You must return your response in a line with only the score.\n",
    "    Do not add any more details.\n",
    "    On a separate line provide your {reasoning} for the score as well.\n",
    "    Return your response following the exact format outlined below.\n",
    "    Do not add or remove anything.\n",
    "    And all of this must be in a valid JSON format.\n",
    "    \n",
    "    {\"answer\": answer,\n",
    "     \"score\": score,\n",
    "     \"reasoning\": reasoning}\n",
    "    \"\"\"\n",
    "assistant_content = \"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f95f9309-1d92-4e43-8de4-f5286d11d4af",
   "metadata": {},
   "source": [
    "We can extract the text from this context and pass it to our LLM to generate a response to the question. We’re also going to ask it to score the quality of its response for the query. To do this, we’ve defined a `QueryAgentWithContext` that inherits from `QueryAgent`, with the change that we’re providing the context and it doesn’t need to retrieve it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7dc33d2e-51b1-4417-8f8d-3699d5840fc0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "class QueryAgentWithContext(QueryAgent):\n",
    "    def __call__(self, query, context):\n",
    "        user_content = f\"query: {query}, context: {context}\"\n",
    "        response = generate_response(\n",
    "            llm=self.llm,\n",
    "            temperature=self.temperature,\n",
    "            stream=False,\n",
    "            system_content=self.system_content,\n",
    "            assistant_content=self.assistant_content,\n",
    "            user_content=user_content[: self.context_length])\n",
    "        return response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63c2db65-36fc-48cb-8e86-18b96554f977",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def get_references(data, llm, temperature, system_content, assistant_content, num_samples=None):\n",
    "    # Initialize agent\n",
    "    agent = QueryAgentWithContext(\n",
    "        llm=llm, \n",
    "        temperature=temperature,\n",
    "        system_content=system_content,\n",
    "        assistant_content=assistant_content)\n",
    "    \n",
    "    results = []\n",
    "    for row in tqdm(data[:num_samples]):\n",
    "        # Generate response\n",
    "        query = row[\"question\"]\n",
    "        context = fetch_text(uri=row[\"source\"])\n",
    "        response = agent(query=query, context=context)\n",
    "\n",
    "        # Extract from response\n",
    "        answer, score, reasoning = extract_from_response(response=response)\n",
    "        result = ({\n",
    "                \"question\": query,\n",
    "                \"source\": row[\"source\"],\n",
    "                \"answer\": answer,\n",
    "                \"score\": score,\n",
    "                \"reasoning\": reasoning,\n",
    "            })\n",
    "        results.append(result)\n",
    "        clear_output(wait=True)\n",
    "        display(JSON(json.dumps(result, indent=2)))\n",
    "    return results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bd52ee15-d3fa-40a0-b3a1-2b28eca8db2e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Refernces\n",
    "REFERENCES_FILE_PATH = Path(EXPERIMENTS_DIR, \"references\", \"gpt-4.json\")\n",
    "REFERENCES_FILE_PATH.parent.mkdir(parents=True, exist_ok=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25cb4b98-ddc7-462e-8d6f-92f703bfc838",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "answer": "You can specify the batch format in the map_batches() function. If you're working with NumPy ndarrays, your function should accept and return a dictionary of ndarrays. If you're working with pandas DataFrames, your function should accept and return a DataFrame. Make sure your function is compatible with the batch format you're using.",
       "question": "I’m struggling a bit with Ray Data type conversions when I do map_batches. Any advice?",
       "reasoning": "The context provides clear instructions on how to handle data type conversions when using the map_batches function in Ray Data. It explains how to configure the batch type and how to ensure your function is compatible with the chosen batch format.",
       "score": 5,
       "source": "https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-format"
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1/1 [00:13<00:00, 13.85s/it]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.519774011299435\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "# gpt-4\n",
    "results = get_references(\n",
    "    data=data, num_samples=NUM_SAMPLES, llm=\"gpt-4\", temperature=0.0, \n",
    "    system_content=system_content, assistant_content=assistant_content)\n",
    "print (np.mean([float(result[\"score\"]) for result in results if result[\"score\"]]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0b8356fb-2a34-4697-b478-36911b582e99",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Save to file\n",
    "with open(REFERENCES_FILE_PATH, \"w\") as fp:\n",
    "    json.dump(results, fp, indent=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c522532-98c2-46e9-b262-1747145f34e1",
   "metadata": {},
   "source": [
    "We can now create a dataset with query, source, response, score and reasoning. We can inspect this to determine if our evaluator is of high quality. We found that `gpt-4` was a high quality evaluator based on the scores and reasonings it provided. We performed the same evaluation with other LLMs (ex. `Llama-2-70b`) and we found that they lacked the appropriate reasoning and were very generous with responses from themselves.\n",
    "\n",
    "**Note**: A more thorough evaluation would also test for the following by asking the evaluator to compare responses from different LLMs across the following:\n",
    "- position (which responses we show first) \n",
    "- verbosity (longer responses are favored) \n",
    "- nepotism (ex. GPT4 prefers GPT 3.5, etc.)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dceefac3-e86c-4f2b-96ee-99e3f026692f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "EVALUATOR = \"gpt-4\"\n",
    "REFERENCES_FILE_PATH = Path(EXPERIMENTS_DIR, \"references\", \"gpt-4.json\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36a5e137-1f4a-4c31-b1af-0c9e48131f82",
   "metadata": {},
   "source": [
    "## Cold start"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67a7f87b-9f90-40c7-85df-2e0c287a5144",
   "metadata": {},
   "source": [
    "We may not always have a prepared dataset of questions and the best source to answer that question readily available. To address this cold start problem, we could use an LLM to look at our text chunks and generate questions that the specific chunk would answer. This provides us with quality questions and the exact source the answer is in. However, this dataset generation method could be a bit noisy. The generated questions may not always have high alignment to what our users may ask. And the specific chunk we say is the best source may also have that exact information in other chunks. Nonetheless, this is a great way to start our development process while we collect + manually label a high quality dataset.\n",
    "\n",
    "<img width=\"800\" src=\"https://images.ctfassets.net/xjan103pcp94/3QR9zkjtpgeqK8XKPteTav/76aa9e7743330e7fcf73b07332a7ddf2/image10.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30844ad6-adb6-43d8-af54-0604e7942816",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Prompt\n",
    "num_questions = 3\n",
    "system_content = f\"\"\"\n",
    "Create {num_questions} questions using only the context provided.\n",
    "End each question with a '?' character and then in a newline write the answer to that question using only the context provided.\n",
    "Separate each question/answer pair by a newline.\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51b0e827-5eaa-4c52-b52e-0d46f39a3666",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Generate questions\n",
    "synthetic_data = []\n",
    "for chunk in chunks[:1]:  # small samples\n",
    "    response = generate_response(\n",
    "        llm=\"gpt-4\",\n",
    "        temperature=0.0,\n",
    "        stream=False,\n",
    "        system_content=system_content,\n",
    "        user_content=f\"context: {chunk.page_content}\")\n",
    "    entries = response.split(\"\\n\\n\")\n",
    "    for entry in entries:\n",
    "        question, answer = entry.split(\"\\n\")\n",
    "        synthetic_data.append({\"question\": question, \"source\": chunk.metadata[\"source\"], \"answer\": answer})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f083eaf6-6c0e-4e8d-a4a2-cc32bea470af",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'question': 'What is the Ray Dashboard used for?',\n",
       "  'source': 'https://docs.ray.io/en/master/ray-observability/getting-started.html#ray-dashboard',\n",
       "  'answer': 'The Ray Dashboard is used for monitoring and debugging Ray applications.'},\n",
       " {'question': 'What does the visual representation of the Ray Dashboard allow users to do?',\n",
       "  'source': 'https://docs.ray.io/en/master/ray-observability/getting-started.html#ray-dashboard',\n",
       "  'answer': 'The visual representation of the Ray Dashboard allows users to track the performance of applications and troubleshoot issues.'},\n",
       " {'question': 'Is the Ray Dashboard web-based?',\n",
       "  'source': 'https://docs.ray.io/en/master/ray-observability/getting-started.html#ray-dashboard',\n",
       "  'answer': 'Yes, the Ray Dashboard is web-based.'}]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "synthetic_data[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b029edf-e018-427f-a1c9-2b4bf689d09c",
   "metadata": {},
   "source": [
    "## Experiments"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dea24ed-0a47-47a6-9881-0af1ea8c5691",
   "metadata": {},
   "source": [
    "With our evaluator set, we're ready to start experimenting with the various components in our LLM application. While we could perform this as a large [hyperparameter tuning experiment](https://docs.ray.io/en/latest/tune/index.html), where we can search across promising combinations of values/decisions, we're going to evaluate one decision at a time and set the best value for the next experiment.\n",
    "\n",
    "**Note**: this approach is slightly imperfect because many of our decisions are not indepedent (ex. `chunk_size` and `num_chunks` should ideally be evaluated across many combinations of values).\n",
    "\n",
    "<img width=\"700\" src=\"https://images.ctfassets.net/xjan103pcp94/2LlTUhNFzfLM775IVSxjkX/af49d7b4e0fdd4a482d29cf6eab5067f/image13.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8b51fd3-7a86-41dd-91bb-adcb83bf7269",
   "metadata": {},
   "source": [
    "### Utilities"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07b3f59f-f79b-4cdb-b55f-6d1d5d245628",
   "metadata": {},
   "source": [
    "Before we start our experiments, we’re going to define a few more utility functions. Our evaluation workflow will use our evaluator to assess the end-to-end quality (`quality_score (overall)`) of our application since the response depends on the retrieved context and the LLM. But we’ll also include a `retrieval_score` to measure the quality of our retrieval process (chunking + embedding). Our logic for determining the `retrieval_score` registers a success if the best source is anywhere in our retrieved num_chunks sources. We don't account for order, exact page section, etc. but we could add those constraints to have a more conservative retrieval score.\n",
    "\n",
    "\n",
    "<img width=\"700\" src=\"https://images.ctfassets.net/xjan103pcp94/2lhpSUNrMmi7WAHpd3wslR/15facf649e30571e8d806d354f475f0b/image6.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a42a1ebb-b3d2-48d7-9d9f-bf5231cfe5b8",
   "metadata": {},
   "source": [
    "We'll set where our labeled data and reference reports are located. We'll be using the former to generate responses and the latter dataset to evaluate those responses."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c084e6dd-b8b7-4492-9346-a3455e26e96f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from rag.generate import generate_responses\n",
    "from rag.evaluate import evaluate_responses"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3734e048-033c-46c3-834f-5cc4b380e00a",
   "metadata": {},
   "source": [
    "Let's define a function to determine our retrieval score, which registers a success if the best source is anywhere in our retrieval `num_chunks` sources. We don't account for order, exact page section, etc. but we could add those constraints to have a more conservative retreival score."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73a57c87-8ef0-4d45-9594-34ca07ff7210",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def get_retrieval_score(references, generated):\n",
    "    matches = np.zeros(len(references))\n",
    "    for i in range(len(references)):\n",
    "        reference_source = references[i][\"source\"].split(\"#\")[0]\n",
    "        if not reference_source:\n",
    "            matches[i] = 1\n",
    "            continue\n",
    "        for source in generated[i][\"sources\"]:\n",
    "            # sections don't have to perfectly match\n",
    "            if reference_source == source.split(\"#\")[0]:\n",
    "                matches[i] = 1\n",
    "                continue\n",
    "    retrieval_score = np.mean(matches)\n",
    "    return retrieval_score"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a7c708e-8fd1-4560-b65f-cf62d0034722",
   "metadata": {},
   "source": [
    "We'll define one encompassing function that will generate and evaluate the responses so that we can run these experiments with one function call. Regardless of what configuration(s) we want to evaluate, we’ll need to first generate responses using that configuration and then evaluate those responses using our evaluator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66a76e95-9c1b-488f-bd9f-750a176d3d77",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def run_experiment(\n",
    "    experiment_name,\n",
    "    chunk_size, chunk_overlap, num_chunks,\n",
    "    embedding_model_name, embedding_dim,\n",
    "    llm, evaluator,\n",
    "    docs_dir, experiments_dir, references_fp,\n",
    "    system_content=\"Answer the query using the context provided. Be succinct.\",\n",
    "    use_lexical_search=False,\n",
    "    lexical_search_k=1,\n",
    "    use_reranking=False,\n",
    "    rerank_threshold=0.0,\n",
    "    rerank_k=7,\n",
    "    num_samples=None,\n",
    "    sql_dump_fp=None):\n",
    "    \"\"\"Generate responses and evaluate them.\"\"\"\n",
    "    \n",
    "    # Generate responses\n",
    "    generate_responses(\n",
    "        experiment_name=experiment_name, \n",
    "        chunk_size=chunk_size, \n",
    "        chunk_overlap=chunk_overlap, \n",
    "        num_chunks=num_chunks,\n",
    "        embedding_model_name=embedding_model_name,\n",
    "        embedding_dim=embedding_dim,\n",
    "        use_lexical_search=use_lexical_search,\n",
    "        lexical_search_k=lexical_search_k,\n",
    "        use_reranking=use_reranking,\n",
    "        rerank_threshold=rerank_threshold,\n",
    "        rerank_k=rerank_k,\n",
    "        llm=llm, \n",
    "        temperature=0.0, \n",
    "        max_context_length=MAX_CONTEXT_LENGTHS[llm], \n",
    "        system_content=system_content,\n",
    "        assistant_content=\"\",\n",
    "        docs_dir=docs_dir,\n",
    "        experiments_dir=experiments_dir,\n",
    "        references_fp=references_fp,\n",
    "        num_samples=num_samples,\n",
    "        sql_dump_fp=sql_dump_fp)\n",
    "\n",
    "    # Evaluate responses\n",
    "    evaluation_system_content = \"\"\"\n",
    "        Your job is to rate the quality of our generated answer {generated_answer}\n",
    "        given a query {query} and a reference answer {reference_answer}.\n",
    "        Your score has to be between 1 and 5.\n",
    "        You must return your response in a line with only the score.\n",
    "        Do not return answers in any other format.\n",
    "        On a separate line provide your reasoning for the score as well.\n",
    "        \"\"\"\n",
    "    evaluate_responses(\n",
    "        experiment_name=experiment_name,\n",
    "        evaluator=evaluator, \n",
    "        temperature=0.0, \n",
    "        max_context_length=MAX_CONTEXT_LENGTHS[evaluator],\n",
    "        system_content=evaluation_system_content,\n",
    "        assistant_content=\"\",\n",
    "        experiments_dir=experiments_dir,\n",
    "        references_fp=references_fp,\n",
    "        responses_fp=str(Path(experiments_dir, \"responses\", f\"{experiment_name}.json\")),\n",
    "        num_samples=num_samples)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "169201fd-aa78-4459-a55f-04c97f1938aa",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def print_experiment(experiment_name, experiments_dir, evaluator=EVALUATOR, verbose=True):\n",
    "    eval_fp = Path(experiments_dir, \"evaluations\", f\"{experiment_name}_{evaluator}.json\")\n",
    "    with open(eval_fp, \"r\") as fp:\n",
    "        d = json.load(fp)\n",
    "    retrieval_score = d[\"retrieval_score\"]\n",
    "    quality_score = d[\"quality_score\"]\n",
    "    if verbose:\n",
    "        print (experiment_name)\n",
    "        print (\"  retrieval score:\", retrieval_score)\n",
    "        print (\"  quality score:\", quality_score)\n",
    "        print ()\n",
    "    return {\"retrieval_score\": retrieval_score, \"quality_score\": quality_score}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f44e646-740e-47b7-a878-e15fb432394d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def plot_scores(scores):\n",
    "    # Prepare data for plotting\n",
    "    experiment_names = list(scores.keys())\n",
    "    retrieval_scores = [scores[experiment_name][\"retrieval_score\"] for experiment_name in experiment_names]\n",
    "    quality_scores = [scores[experiment_name][\"quality_score\"] for experiment_name in experiment_names]\n",
    "    \n",
    "    # Plotting\n",
    "    plt.figure(figsize=(10, 3))\n",
    "    for i, experiment_name in enumerate(experiment_names):\n",
    "        plt.scatter(quality_scores[i], retrieval_scores[i], label=experiment_name)\n",
    "        plt.text(quality_scores[i]+0.005, retrieval_scores[i]+0.005, experiment_name, ha=\"right\")\n",
    "        \n",
    "    # Add labels and title\n",
    "    plt.xlabel(\"Quality Score\")\n",
    "    plt.ylabel(\"Retrieval Score\")\n",
    "    plt.legend(title=\"Experiments\")\n",
    "    \n",
    "    # Show the plot\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8accfe8a-5041-4b94-a225-9e34a73ac5b8",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "llm = \"mistralai/Mixtral-8x7B-Instruct-v0.1\"\n",
    "embedding_model_name = \"thenlper/gte-base\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b31ebee-f839-4aaa-b328-72cee088c830",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Context"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e89f56c4-2629-4ff7-b57c-f319170937de",
   "metadata": {},
   "source": [
    "We're first going to test if the additonal context we provide is helpful at all. This is to validate that the RAG system is indeed worth the effort. We can do this by settings `num_chunks=0` (no context) and comparing that to `num_chunks=5`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91cefda6-0ec7-40a2-afc0-b8af2bdd3332",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Without context\n",
    "num_chunks = 0\n",
    "experiment_name = f\"without-context\"\n",
    "embedding_model_name = \"thenlper/gte-base\"\n",
    "run_experiment(\n",
    "    experiment_name=experiment_name, \n",
    "    chunk_size=300, \n",
    "    chunk_overlap=50,\n",
    "    num_chunks=num_chunks,\n",
    "    embedding_model_name=embedding_model_name,\n",
    "    embedding_dim=EMBEDDING_DIMENSIONS[embedding_model_name],\n",
    "    llm=llm,\n",
    "    evaluator=EVALUATOR,\n",
    "    docs_dir=DOCS_DIR, \n",
    "    experiments_dir=EXPERIMENTS_DIR, \n",
    "    references_fp=REFERENCES_FILE_PATH,\n",
    "    num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a819c26a-9e05-484b-8e57-edfd26a84d4d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica, you can follow these steps:\n\n1. Get a handle to the serve replica using `ray.get_actor()` with the appropriate NAME and namespace.\n2. Kill the replica using `ray.kill()` and set `no_restart=True`.\n\nHere is an example:\n```python\nimport ray\n\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\nray.kill(replica_handle, no_restart=True)\n```\nNote that you can get the NAME of the replica from the output of `ray list actors` command.\n\nIf you are running KubeRay, you can exec into a Ray pod before running these commands.\n\nAlso, you can simulate replica failures by manually killing deployment replicas using the `ray summary actors` command.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer is highly detailed and provides a step-by-step guide on how to kill a specific serve replica. It even includes a code example for better understanding. The reference answer does not provide any useful information, making the generated answer superior.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 5,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#scaling-horizontally-with-num-replicas",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [25:46<00:00,  8.74s/it]\n"
     ]
    }
   ],
   "source": [
    "# With context\n",
    "num_chunks = 5\n",
    "experiment_name = \"with-context\"\n",
    "embedding_model_name = \"thenlper/gte-base\"\n",
    "run_experiment(\n",
    "    experiment_name=experiment_name, \n",
    "    chunk_size=300, \n",
    "    chunk_overlap=50, \n",
    "    num_chunks=num_chunks,\n",
    "    embedding_model_name=embedding_model_name,\n",
    "    embedding_dim=EMBEDDING_DIMENSIONS[embedding_model_name],\n",
    "    llm=llm,\n",
    "    evaluator=EVALUATOR,\n",
    "    docs_dir=DOCS_DIR, \n",
    "    experiments_dir=EXPERIMENTS_DIR, \n",
    "    references_fp=REFERENCES_FILE_PATH,\n",
    "    num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8327cf28-5114-4e93-b769-014108dd743a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "without-context\n",
      "  retrieval score: 0.0\n",
      "  quality score: 3.194915254237288\n",
      "\n",
      "with-context\n",
      "  retrieval score: 0.5254237288135594\n",
      "  quality score: 3.5112994350282487\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1oAAAEoCAYAAABB14B9AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABRa0lEQVR4nO3deVxU1f8/8NewDDsDiLIoiQuKmIiK8kETUEnRcquUDBXJ5ZdbKlpKmWi55565ZAJmftIWUzNDjY/Dx4UEF3DNhQ8KKqCEbCKLM/f3B19vToCCzp0BfT0fj3nIPffcc953Dtd8d+49VyYIggAiIiIiIiLSGgN9B0BERERERPS8YaJFRERERESkZUy0iIiIiIiItIyJFhERERERkZYx0SIiIiIiItIyJlpERERERERaxkSLiIiIiIhIy5hoERERERERaRkTLSIiIiIiIi1jokVEREREVMfFxMTAxsbmifVkMhl27doleTz0ZEb6DqCuU6vVuHXrFqysrCCTyfQdDhERERG9gPr27Yvu3bujoKAAALBo0SL8+uuvOHLkSKW6xcXFYr264nHx1sV2qyMIAgoLC+Hs7AwDg8fPWckEQRB0ElU9dePGDbi4uOg7DCIiIiIiqiMyMjLQpEmTx9bhjNYTWFlZAaj4Mq2trfUcDRERERE9L2JjYzFu3DikpaXB0NAQZ86cQffu3TF16lTMmzcPADBp0iSUlpYiICAAERERSE9Px7Zt2zBhwgSNttatW4eQkBAoFAqsWbMGBw4cQFxcHJycnLBgwQL069fvsbH88ccf+Oyzz3Dy5EnI5XJ06tQJUVFRsLW1RWlpKT755BP89NNPKCwsRIcOHbBw4UJ06tQJAHD48GG8/vrr2L17NyIjI3Hp0iW0a9cO69atg5ub22PjzcvLw+zZs7Fv3z6UlZXBy8sLixYtQrt27ZCTkwNfX1/8v//3/zBjxgwAwPHjx/Haa6/hxx9/xM2bN6ttVyoFBQVwcXERc4THEuix8vPzBQBCfn6+vkMhIiIioudIXl6eYGBgICQlJQmCIAirVq0S7O3tBR8fH7FOy5YthU2bNgnR0dGCQqEQBEEQiouLhenTpwtt27YVMjMzhczMTKG4uFgQBEEAIDRp0kT497//LVy5ckV4//33BUtLS+Gvv/6qNo7Tp08LJiYmwvjx44Xk5GTh3LlzwhdffCHcuXNHEARBeP/99wVnZ2dh3759wvnz54XQ0FDB1tZWbPPQoUMCAMHHx0dQKpXC+fPnhe7duwtdu3Z9YryBgYFC//79haSkJOHy5cvC9OnThQYNGoht//rrr4KxsbGQlJQkFBQUCM2bNxemTZv2xHalUpvcgInWEzDRIiIiIiKpdOzYUfj8888FQRCEQYMGCQsWLBDkcrlQWFgo3LhxQwAgXL58WSPREgRBiIyMFNq3b1+pPQDC7Nmzxe2ioiIBgPDbb79VG8OwYcOEbt26VbmvqKhIMDY2FrZt2yaWlZWVCc7OzsLSpUsFQfg70fr999/FOr/++qsAQLh//3618R4+fFiwtrYWSkpKNMpbtGghbNy4UdyeMGGC0KpVK+Gdd94R2rVrp1G/uu9BKrXJDbjqIBERERGRnvj7+0OpVEIQBBw+fBhvvPEG2rRpgyNHjiA+Ph7Ozs5wc3OrVZuenp7izxYWFrC2tsbt27cBAG3btoWlpSUsLS3Rt29fAEBycjJ69epVZVupqakoLy9Ht27dxDJjY2N06dIFFy9erLZfJycnABD7rUpKSgqKiorQoEEDMSZLS0ukpaUhNTVVrLds2TI8ePAAP/zwA7Zt2wYTE5OafhV6xWe0iIiIiIj0JCAgAFFRUUhJSYGxsTHc3d0REBAApVKJu3fvwt/fv9ZtGhsba2zLZDKo1WoAwL59+1BeXg4AMDMz0/jzWT3a78PVuh/2W5WioiI4OTlBqVRW2icuZa9WITX+e9y6mQG1Wo1r/0tFu3bttBKv1JhoaYEgCHjw4AFUKpW+Q6EXhKGhIYyMjPjKASIionque/fuKCwsxMqVK8WkKiAgAIsXL8bdu3cxffr0Ko+Ty+VP9W/Ppk2bVirz9PREXFycuADHo1q0aAG5XI6jR4+Kx5aXlyMpKQlTp06tcb9VxduxY0dkZWXByMgIrq6ulQ+6sAdlez/E8BVXEOxuiNYNjDBm+Fs4uy8KjbqPrLbduoKJ1jMqKytDZmYmiouL9R0KvWDMzc3h5OQEuVyu71CIiIjoKdna2sLT0xPbtm3D2rVrAQB+fn4YOnQoysvLq53RcnV1RVpaGpKTk9GkSRNYWVk99S11ERERaNeuHSZMmID33nsPcrkchw4dwpAhQ2Bvb4/x48fjgw8+gJ2dHV566SUsXboUxcXFGD16dI37qCrewMBA+Pr6YtCgQVi6dClatWqFW7du4ddff8Xg9nbwvvAZPj5wH/mlAtb0NYWlHNh39QHeHT0ae3fZAB4DtPo9aBsTrWegVqvF5TidnZ0hl8s5w0CSEwQBZWVluHPnDtLS0uDm5vbEF+YRERFR3eXv74/k5GQEBAQAAOzs7ODh4YHs7Gy0bt26ymPefPNN7Ny5Ez169EBeXh6io6MxatSop+q/VatWOHDgAD766CN06dIFZmZm8PHxwbBhwwAAixcvhlqtxogRI1BYWAhvb2/s378ftra2Ne6junj37duHjz/+GGFhYbhz5w4cHR3h1707HAx2QJlRjlXHy3Ao1BzWJhX/xt462AztNxRh/Zz/h/Hfv6bV70Hb+MLiJygoKIBCoUB+fn6l92iVlJQgLS0NTZs2hbm5uZ4ipBdVcXExrl+/jmbNmsHU1FTf4RARERFpR9phYMvrT64Xuhdo1l36eB7xuNzgn/i/wbWAswmkD/y9IyIioudSUbZ26+kJ/6VGRERERER1h6WDduvpCRMtIiIiIiKqO5p2BaydAVS39oEMsG5cUa8OY6JFOjdq1CgMGjRI32EQERERUV1kYAgELfm/jX8mW/+3HbS4ol4dxkTrOTVq1CjIZLJKn6CgIH2HhtWrVyMmJkbfYQCoeJnerl279B0GERERET3KYwAw9BvA2kmz3Nq5otxjgH7iqgUu7/4cCwoKQnR0tEaZPt8roFKpIJPJoFAo9BYDEREREdUTHgMA99eA68cqFr6wdKi4XbCOz2Q9xBmt55iJiQkcHR01Pra2tlAqlZDL5Th8+LBYd+nSpWjUqBGysytWbwkICMCkSZMwadIkKBQK2Nvb45NPPsGjbwMoLS3FjBkz0LhxY1hYWMDHxwdKpVLcHxMTAxsbG+zZswceHh4wMTFBenp6pVsHAwICMHnyZEydOhW2trZwcHDApk2bcO/ePYSFhcHKygotW7bEb7/9pnF+586dQ9++fWFpaQkHBweMGDECOTk5Gu2+//77+PDDD2FnZwdHR0fMnTtX3P/wDeSDBw+GTCYTt1NSUtCjRw9YWVnB2toanTp1wokTJ55xNIiIiIio1gwMK5Zwb/dWxZ/1JMkC6mGi9eWXX8LV1RWmpqbw8fFBYmJitXVjYmIq3TrH9w1VJCBTp07FiBEjkJ+fj9OnT+OTTz7B119/DQeHv1dv2bJlC4yMjJCYmIjVq1djxYoV+Prrr8X9kyZNQkJCArZv344zZ85gyJAhCAoKwpUrV8Q6xcXFWLJkCb7++mucP38ejRo1qjKmLVu2wN7eHomJiZg8eTLGjx+PIUOGoGvXrjh16hR69+6NESNGoLi4GACQl5eHnj17okOHDjhx4gRiY2ORnZ2NoUOHVmrXwsICx48fx9KlS/Hpp5/i4MGDAICkpCQAQHR0NDIzM8XtkJAQNGnSBElJSTh58iRmzZoFY2NjLXzzRERERPTCEOqR7du3C3K5XIiKihLOnz8vjB07VrCxsRGys7OrrB8dHS1YW1sLmZmZ4icrK6tWfebn5wsAhPz8/Er77t+/L1y4cEG4f//+U52PlEJDQwVDQ0PBwsJC47NgwQJBEAShtLRU8PLyEoYOHSp4eHgIY8eO1Tje399faNOmjaBWq8WymTNnCm3atBEEQRCuX78uGBoaCjdv3tQ4rlevXkJERIQgCBXfPwAhOTm5UmwDBw7U6OuVV14Rtx88eCBYWFgII0aMEMsyMzMFAEJCQoIgCILw2WefCb1799ZoNyMjQwAgXLp0qcp2BUEQOnfuLMycOVPcBiD8/PPPGnWsrKyEmJgYoa6ry79/RERERM+jx+UG/1SvntFasWIFxo4di7CwMADAhg0b8OuvvyIqKgqzZs2q8hiZTAZHR8ca91FaWorS0lJxu6Cg4NmC1qMePXpg/fr1GmV2dnYAALlcjm3btsHT0xNNmzbFypUrKx3/r3/9CzLZ3yu9+Pr6Yvny5VCpVDh79ixUKhVatWqlcUxpaSkaNGggbsvlcnh6ej4x1kfrGBoaokGDBmjXrp1Y9nCm7fbt2wAqbu87dOgQLC0tK7WVmpoqxvXPvp2cnMQ2qhMeHo4xY8Zg69atCAwMxJAhQ9CiRYsnngMRERER0UP1JtEqKyvDyZMnERERIZYZGBggMDAQCQkJ1R5XVFSEpk2bQq1Wo2PHjli4cCHatm1bbf1FixZh3rx5Wo1dXywsLNCyZctq9x87dgwAkJubi9zcXFhYWNS47aKiIhgaGuLkyZMwNNS8V/bR5MfMzEwjWavOP2/Nk8lkGmUP21Cr1WL//fv3x5IlS/BPTk5/r05TVbsP26jO3Llz8c477+DXX3/Fb7/9hsjISGzfvh2DBw9+4nkQEREREQH16BmtnJwcqFQqjWeIgIqZjqysrCqPad26NaKiorB79258++23UKvV6Nq1K27cuFFtPxEREcjPzxc/GRkZWj2PuiI1NRXTpk3Dpk2b4OPjg9DQ0EoJyPHjxzW2//jjD7i5ucHQ0BAdOnSASqXC7du30bJlS41PbWYQn1bHjh1x/vx5uLq6Vuq/NgmjsbExVCpVpfJWrVph2rRpOHDgAN54441KqzcSERERET1OvUm0noavry9GjhwJLy8v+Pv7Y+fOnWjYsCE2btxY7TEmJiawtrbW+NRXpaWlyMrK0vg8TFiHDx+OPn36ICwsDNHR0Thz5gyWL1+ucXx6ejrCw8Nx6dIlfPfdd/jiiy8wZcoUABWJSEhICEaOHImdO3ciLS0NiYmJWLRoEX799VfJz23ixInIzc3FsGHDkJSUhNTUVOzfvx9hYWFVJk7VcXV1RVxcHLKysnD37l3cv38fkyZNglKpxPXr13H06FEkJSWhTZs2Ep4NERERET1v6s2tg/b29jA0NBSXH38oOzu7xjMoxsbG6NChA65evSpFiHVObGysxm10QMUs3zvvvIPr169j7969ACputfvqq68wbNgw9O7dG+3btwcAjBw5Evfv30eXLl1gaGiIKVOmYNy4cWJb0dHRmD9/PqZPn46bN2/C3t4e//rXv/D6669Lfm7Ozs44evQoZs6cid69e6O0tBRNmzZFUFAQDAxq/v8Pli9fjvDwcGzatAmNGzfG5cuX8ddff2HkyJHIzs6Gvb093njjjefmdlIiIiIi0g2ZIDzyYqQ6zsfHB126dMEXX3wBoOJ5nZdeegmTJk2qdjGMR6lUKrRt2xb9+vXDihUratRnQUEBFAoF8vPzK81ulZSUIC0tDc2aNXvulo0PCAiAl5cXVq1ape9QqBrP8+8fERERUV30uNzgn+rNjBZQsRpcaGgovL290aVLF6xatUp8qS1QMQPTuHFjLFq0CADw6aef4l//+hdatmyJvLw8fP7557h+/TrGjBmjz9MgIiIiIqLnXL1KtIKDg3Hnzh3MmTMHWVlZ8PLyQmxsrLhARnp6usZtY3fv3sXYsWORlZUFW1tbdOrUCceOHYOHh4e+ToGIiIiIiF4A9erWQX14UW8dpLqPv39EREREulWbWwef61UHiYiIiIiI9IGJFhERERERkZYx0SIiIiIiItIyJlpERERERERaxkSLiIiIiIhIy5hoERERERERaRkTLXqsmJgY2NjYPLGeTCbDrl27JI+HiIiIiKg+YKJFjxUcHIzLly+L23PnzoWXl5fe4qlLCV1AQACmTp1ab9olIiIiIt0x0ncAVEGlFpCYlovbhSVoZGWKLs3sYGgg03dYMDMzg5mZmb7DICIiIiKqVzijVQfEnsvEK0v+g2Gb/sCU7ckYtukPvLLkP4g9lylJf3v37oWNjQ1UKhUAIDk5GTKZDLNmzRLrjBkzBsOHD9e4dTAmJgbz5s1DSkoKZDIZZDIZYmJixGNycnIwePBgmJubw83NDXv27NHoNz4+Hl26dIGJiQmcnJwwa9YsPHjwQNzv6uqKVatWaRzj5eWFuXPnivsBYPDgwZDJZOJ2daKiotC2bVuxv0mTJon70tPTMXDgQFhaWsLa2hpDhw5Fdna2uP/hzN3WrVvh6uoKhUKBt99+G4WFhQCAUaNGIT4+HqtXrxa/i2vXrgEAzp07h759+8LS0hIODg4YMWIEcnJyAABKpRJyuRyHDx8W+1q6dCkaNWqE7Ozsx7ZLRERERPUHEy09iz2XifHfnkJmfolGeVZ+CcZ/e0qSZKt79+4oLCzE6dOnAVQkQPb29lAqlWKd+Ph4BAQEaBwXHByM6dOno23btsjMzERmZiaCg4PF/fPmzcPQoUNx5swZ9OvXDyEhIcjNzQUA3Lx5E/369UPnzp2RkpKC9evXY/PmzZg/f36N405KSgIAREdHIzMzU9yuyvr16zFx4kSMGzcOZ8+exZ49e9CyZUsAgFqtxsCBA5Gbm4v4+HgcPHgQ//vf/zTOBQBSU1Oxa9cu7N27F3v37kV8fDwWL14MAFi9ejV8fX0xduxY8btwcXFBXl4eevbsiQ4dOuDEiROIjY1FdnY2hg4dCuDv2wJHjBiB/Px8nD59Gp988gm+/vprODg4VNsuEREREdUvvHVQj1RqAfN+uQChin0CABmAeb9cwKsejlq9jVChUMDLywtKpRLe3t5QKpWYNm0a5s2bh6KiIuTn5+Pq1avw9/fH0aNHxePMzMxgaWkJIyMjODo6Vmp31KhRGDZsGABg4cKFWLNmDRITExEUFIR169bBxcUFa9euhUwmg7u7O27duoWZM2dizpw5MDB4cs7fsGFDAICNjU2V/T9q/vz5mD59OqZMmSKWde7cGQAQFxeHs2fPIi0tTUxivvnmG7Rt2xZJSUliPbVajZiYGFhZWQEARowYgbi4OCxYsAAKhQJyuRzm5uYasaxduxYdOnTAwoULxbKoqCi4uLjg8uXLaNWqFebPn4+DBw9i3LhxOHfuHEJDQzFgwABxbKpql4iIiIjqF85o6VFiWm6lmaxHCQAy80uQmJar9b79/f2hVCohCAIOHz6MN954A23atMGRI0cQHx8PZ2dnuLm51apNT09P8WcLCwtYW1vj9u3bAICLFy/C19cXMtnfCWO3bt1QVFSEGzduPPV5pKenw9LSUvwsXLgQt2/fxq1bt9CrV68qj7l48SJcXFw0Zoo8PDxgY2ODixcvimWurq5ikgUATk5O4vlUJyUlBYcOHdKIyd3dHUDFDBkAyOVybNu2DT/99BNKSkqwcuXKpz5/IiIiIqqbOKOlR7cLq0+ynqZebQQEBCAqKgopKSkwNjaGu7s7AgICoFQqcffuXfj7+9e6TWNjY41tmUwGtVpd4+MNDAwgCJrze+Xl5Y89xtnZGcnJyeK2nZ1dpTie1tOcT1FREfr3748lS5ZU2ufk5CT+fOzYMQBAbm4ucnNzYWFhoYWIiYiIiKiu4IyWHjWyMtVqvdp4+JzWypUrxaTqYaKlVCorPZ/1kFwuFxfRqI02bdogISFBI5E6evQorKys0KRJEwAVtwZmZv79TFpBQQHS0tI02jE2Ntbo38jICC1bthQ/dnZ2sLKygqurK+Li4qqNJSMjAxkZGWLZhQsXkJeXBw8PjxqfU1XfRceOHXH+/Hm4urpqxNWyZUsxmUpNTcW0adOwadMm+Pj4IDQ0VCOBe9rvmIiIiIjqDiZaetSlmR2cFKao7ukrGQAnRcVS79pma2sLT09PbNu2TUyq/Pz8cOrUKVy+fLnaGS1XV1ekpaUhOTkZOTk5KC0trVF/EyZMQEZGBiZPnow///wTu3fvRmRkJMLDw8Xns3r27ImtW7fi8OHDOHv2LEJDQ2FoaFip/7i4OGRlZeHu3bvV9jd37lwsX74ca9aswZUrV3Dq1Cl88cUXAIDAwEC0a9cOISEhOHXqFBITEzFy5Ej4+/vD29u7RufzMJbjx4/j2rVryMnJgVqtxsSJE5Gbm4thw4YhKSkJqamp2L9/P8LCwqBSqaBSqTB8+HD06dMHYWFhiI6OxpkzZ7B8+fLHtktERERE9QsTLT0yNJAhsn/FDMo/k62H25H9PSR7n5a/vz9UKpWYaNnZ2cHDwwOOjo5o3bp1lce8+eabCAoKQo8ePdCwYUN89913NeqrcePG2LdvHxITE9G+fXu89957GD16NGbPni3WiYiIgL+/P15//XW89tprGDRoEFq0aKHRzvLly3Hw4EG4uLigQ4cO1fYXGhqKVatWYd26dWjbti1ef/11XLlyBUDFLYC7d++Gra0t/Pz8EBgYiObNm2PHjh01OpeHZsyYAUNDQ3h4eKBhw4ZIT0+Hs7Mzjh49CpVKhd69e6Ndu3aYOnUqbGxsYGBggAULFuD69evYuHEjgIrbCb/66ivMnj0bKSkp1bZLRERERPWLTPjnQzGkoaCgAAqFAvn5+bC2ttbYV1JSgrS0NDRr1gympk9/e1/suUzM++WCxsIYTgpTRPb3QNDLTo85kl5k2vr9IyIiIqKaeVxu8E9cDKMOCHrZCa96OCIxLRe3C0vQyKridkGpZrKIiIiIiEhaTLTqCEMDGXxbNNB3GEREREREpAV8RouIiIiIiEjLmGgRERERERFpGRMtIiIiIiIiLWOiRUREREREpGVMtIiIiIiIiLSMiRYREREREZGWMdEiIiIiIiLSMiZa9FgxMTGwsbF5Yj2ZTIZdu3ZJHg8RERERUX3ARKuuUKuAtMPA2R8r/lSr9B0RACA4OBiXL18Wt+fOnQsvLy/9BVRLUsVb374HIiIiItKtepdoffnll3B1dYWpqSl8fHyQmJhYo+O2b98OmUyGQYMGSRvg07iwB1j1MrDldeCn0RV/rnq5olzPzMzM0KhRI32HQURERERUr9SrRGvHjh0IDw9HZGQkTp06hfbt26NPnz64ffv2Y4+7du0aZsyYge7du+so0lq4sAf4fiRQcEuzvCCzolyCZGvv3r2wsbGBSlUxa5acnAyZTIZZs2aJdcaMGYPhw4dr3DoYExODefPmISUlBTKZDDKZDDExMeIxOTk5GDx4MMzNzeHm5oY9e54c+9GjRxEQEABzc3PY2tqiT58+uHv3LgCgtLQU77//Pho1agRTU1O88sorSEpKEo9VKpWQyWSIi4uDt7c3zM3N0bVrV1y6dOmJ8ebl5WHMmDFo2LAhrK2t0bNnT6SkpAAA7ty5A0dHRyxcuFDs69ixY5DL5YiLi3vi90BEREREVK8SrRUrVmDs2LEICwuDh4cHNmzYAHNzc0RFRVV7jEqlQkhICObNm4fmzZs/sY/S0lIUFBRofCSjVgGxMwEIVez8v7LYWVq/jbB79+4oLCzE6dOnAQDx8fGwt7eHUqkU68THxyMgIEDjuODgYEyfPh1t27ZFZmYmMjMzERwcLO6fN28ehg4dijNnzqBfv34ICQlBbm5utXEkJyejV69e8PDwQEJCAo4cOYL+/fuLCeCHH36In376CVu2bMGpU6fQsmVL9OnTp1KbH3/8MZYvX44TJ07AyMgI77777hPjHTJkCG7fvo3ffvsNJ0+eRMeOHdGrVy/k5uaiYcOGiIqKwty5c3HixAkUFhZixIgRmDRpEnr16vXE74GIiIiIqN4kWmVlZTh58iQCAwPFMgMDAwQGBiIhIaHa4z799FM0atQIo0ePrlE/ixYtgkKhED8uLi7PHHu1rh+rPJOlQQAKblbU0yKFQgEvLy8xsVIqlZg2bRpOnz6NoqIi3Lx5E1evXoW/v7/GcWZmZrC0tISRkREcHR3h6OgIMzMzcf+oUaMwbNgwtGzZEgsXLkRRUdFjb+1cunQpvL29sW7dOrRv3x5t27bFpEmTYG9vj3v37mH9+vX4/PPP0bdvX3h4eGDTpk0wMzPD5s2bNdpZsGAB/P394eHhgVmzZuHYsWMoKSmpNt4jR44gMTERP/zwA7y9veHm5oZly5bBxsYGP/74IwCgX79+GDt2LEJCQvDee+/BwsICixYtqtH3QERERERUbxKtnJwcqFQqODg4aJQ7ODggKyurymOOHDmCzZs3Y9OmTTXuJyIiAvn5+eInIyPjmeJ+rKJs7darBX9/fyiVSgiCgMOHD+ONN95AmzZtcOTIEcTHx8PZ2Rlubm61atPT01P82cLCAtbW1uJtnW3btoWlpSUsLS3Rt29fAH/PaFUlNTUV5eXl6Natm1hmbGyMLl264OLFi9X26+TkBACPvZ00JSUFRUVFaNCggRiTpaUl0tLSkJqaKtZbtmwZHjx4gB9++AHbtm2DiYlJTb8KIiIiInrBGek7AKk8vN1r06ZNsLe3r/FxJiYmuvsHtaXDk+vUpl4tBAQEICoqCikpKTA2Noa7uzsCAgKgVCpx9+7dSrNZNWFsbKyxLZPJoFarAQD79u1DeXk5AIizP9qaBXq0X5lMBgBiv1UpKiqCk5OTxq2SDz26lH1qaipu3boFtVqNa9euoV27dlqJl4iIiIief/Um0bK3t4ehoSGyszVnd7Kzs+Ho6FipfmpqKq5du4b+/fuLZQ//8W1kZIRLly6hRYsW0gb9JE27AtbOFQtfVPmclqxif9OuWu/64XNaK1euFJOqgIAALF68GHfv3sX06dOrPE4ul4vPUNVG06ZNK5V5enoiLi4O8+bNq7SvRYsWkMvlOHr0qHhseXk5kpKSMHXq1Br3W1W8HTt2RFZWFoyMjODq6lrlcWVlZRg+fDiCg4PRunVrjBkzBmfPnhVXYHza74GIiIiIXgz15tZBuVyOTp06IS4uTixTq9WIi4uDr69vpfru7u44e/YskpOTxc+AAQPQo0cPJCcnS/vsVU0ZGAJBS/5vQ/aPnf+3HbS4op6W2drawtPTE9u2bRMXvfDz88OpU6dw+fLlame0XF1dkZaWhuTkZOTk5KC0tPSpY4iIiEBSUhImTJiAM2fO4M8//8T69euRk5MDCwsLjB8/Hh988AFiY2Nx4cIFjB07FsXFxTV+3q66eAMDA+Hr64tBgwbhwIEDuHbtGo4dO4aPP/4YJ06cAFCxwEZ+fj7WrFmDmTNnolWrVuIiG9r+HoiIiIjo+aOTROvBgwf4/fffsXHjRhQWFgIAbt26haKiolq1Ex4ejk2bNmHLli24ePEixo8fj3v37iEsLAwAMHLkSERERAAATE1N8fLLL2t8bGxsYGVlhZdffhlyuVy7J/m0PAYAQ78BrJ00y62dK8o9BkjWtb+/P1QqlZho2dnZwcPDA46OjmjdunWVx7z55psICgpCjx490LBhQ3z33XdP3X+rVq1w4MABpKSkoEuXLvD19cXu3bthZFQx0bp48WK8+eabGDFiBDp27IirV69i//79sLW1rXEfVcUrk8mwb98++Pn5ISwsDK1atcLbb7+N69evw8HBAUqlEqtWrcLWrVthbW0NAwMDbN26FYcPH8b69eu1/j0QERER0fNHJghCVfesac3169cRFBSE9PR0lJaW4vLly2jevDmmTJmC0tJSbNiwoVbtrV27Fp9//jmysrLg5eWFNWvWwMfHB0DFrW+urq7VvtNo1KhRyMvLw65du2rcX0FBARQKBfLz82Ftba2xr6SkBGlpaWjWrBlMTU1rdR6VqFUVqwsWZVc8k9W0qyQzWfT80OrvHxERERE90eNyg3+SPNEaNGgQrKyssHnzZjRo0AApKSlo3rw5lEolxo4diytXrkjZ/TPTWaJFVEv8/SMiIiLSrdokWpIvhnH48GEcO3as0q16rq6uuHnzptTdExERERER6Zzkz2ip1eoqV2e7ceMGrKyspO6eiIiIiIhI5yRPtHr37o1Vq1aJ2zKZDEVFRYiMjES/fv2k7p6IiIiIiEjnJL91cNmyZQgKCoKHhwdKSkrwzjvv4MqVK7C3t39uVmqT+DE3oirx946IiIio7pI80XJxcUFKSgp27NiBlJQUFBUVYfTo0QgJCYGZmZnU3UvK2NgYAFBcXFzvz4Xqn+LiYgB//x4SERERUd0haaJVXl4Od3d37N27FyEhIQgJCZGyO50zNDSEjY0Nbt++DQAwNzeHTPbPFw8TaZcgCCguLsbt27dhY2MDQ0O+BoCIiIiorpE00TI2NkZJSYmUXeido6MjAIjJFpGu2NjYiL9/RERERFS3SH7r4MSJE7FkyRJ8/fXXMDKSvDudk8lkcHJyQqNGjVBeXq7vcOgFYWxszJksIiIiojpM8swnKSkJcXFxOHDgANq1awcLCwuN/Tt37pQ6BJ0wNDTkP3yJiIiIiAiADhItGxsbvPnmm1J3Q0REREREVGdInmhFR0dL3QUREREREVGdorOHpu7cuYNLly4BAFq3bo2GDRvqqmsiIiIiIiKdMpC6g3v37uHdd9+Fk5MT/Pz84OfnB2dnZ4wePVp8DxAREREREdHzRPJEKzw8HPHx8fjll1+Ql5eHvLw87N69G/Hx8Zg+fbrU3RMREREREemcTBAEQcoO7O3t8eOPPyIgIECj/NChQxg6dCju3LkjZffPrKCgAAqFAvn5+bC2ttZ3OEREREREpCe1yQ0kn9EqLi6Gg4NDpfJGjRrx1kEiIiIiInouSZ5o+fr6IjIyEiUlJWLZ/fv3MW/ePPj6+krdPRERERERkc5Jvurg6tWr0adPHzRp0gTt27cHAKSkpMDU1BT79++XunsiIiIiIiKdk/wZLaDi9sFt27bhzz//BAC0adMGISEhMDMzk7rrZ8ZntIiIiIiICKhdbqCT92iZm5tj7NixuuiKiIiIiIhI7yR/RmvRokWIioqqVB4VFYUlS5ZI3T0REREREZHOSZ5obdy4Ee7u7pXK27Ztiw0bNkjdPRERERERkc5JnmhlZWXBycmpUnnDhg2RmZkpdfdEREREREQ6J3mi5eLigqNHj1YqP3r0KJydnaXunoiIiIiISOckXwxj7NixmDp1KsrLy9GzZ08AQFxcHD788ENMnz5d6u6JiIiIiIh0TvJE64MPPsBff/2FCRMmoKysDABgamqKmTNnIiIiQuruiYiIiIiIdE4n79ECgKKiIly8eBFmZmZwc3ODiYmJLrp9ZnyPFhERERERAbXLDSR/RushS0tLdO7cGVZWVkhNTYVardZV10RERERERDolWaIVFRWFFStWaJSNGzcOzZs3R7t27fDyyy8jIyNDqu6JiIiIiIj0RrJE66uvvoKtra24HRsbi+joaHzzzTdISkqCjY0N5s2bV+t2v/zyS7i6usLU1BQ+Pj5ITEystu7OnTvh7e0NGxsbWFhYwMvLC1u3bn2q8yEiIiIiIqopyRKtK1euwNvbW9zevXs3Bg4ciJCQEHTs2BELFy5EXFxcrdrcsWMHwsPDERkZiVOnTqF9+/bo06cPbt++XWV9Ozs7fPzxx0hISMCZM2cQFhaGsLAw7N+//5nOjYiIiIiI6HEkS7Tu37+v8YDYsWPH4OfnJ243b94cWVlZtWpzxYoVGDt2LMLCwuDh4YENGzbA3NwcUVFRVdYPCAjA4MGD0aZNG7Ro0QJTpkyBp6cnjhw5Um0fpaWlKCgo0PgQERERERHVhmSJVtOmTXHy5EkAQE5ODs6fP49u3bqJ+7OysqBQKGrcXllZGU6ePInAwECxzMDAAIGBgUhISHji8YIgIC4uDpcuXdJI+P5p0aJFUCgU4sfFxaXGMRIREREREQESvkcrNDQUEydOxPnz5/Gf//wH7u7u6NSpk7j/2LFjePnll2vcXk5ODlQqFRwcHDTKHRwc8Oeff1Z7XH5+Pho3bozS0lIYGhpi3bp1ePXVV6utHxERgfDwcHG7oKCAyRYREREREdWKZInWhx9+iOLiYuzcuROOjo744YcfNPYfPXoUw4YNk6p7kZWVFZKTk1FUVIS4uDiEh4ejefPmCAgIqLK+iYlJvXnHFxERERER1U06e2HxsyorK4O5uTl+/PFHDBo0SCwPDQ1FXl4edu/eXaN2xowZg4yMjBoviMEXFhMREREREVBHX1j8rORyOTp16qSxUqFarUZcXBx8fX1r3I5arUZpaakUIRIREREREQGQ8NZBKYSHhyM0NBTe3t7o0qULVq1ahXv37iEsLAwAMHLkSDRu3BiLFi0CULGwhbe3N1q0aIHS0lLs27cPW7duxfr16/V5GkRERERE9JyrV4lWcHAw7ty5gzlz5iArKwteXl6IjY0VF8hIT0+HgcHfk3T37t3DhAkTcOPGDZiZmcHd3R3ffvstgoOD9XUKRERERET0Aqg3z2jpC5/RIiIiIiIi4Dl9RouIiIiIiKi+kOTWwUffQ/UkK1askCIEIiIiIiIivZEk0Tp9+nSN6slkMim6JyIiIiIi0itJEq1Dhw5J0SwREREREVG9wGe0iIiIiIiItEwny7ufOHEC33//PdLT01FWVqaxb+fOnboIgYiIiIiISGckn9Havn07unbtiosXL+Lnn39GeXk5zp8/j//85z9QKBRSd09ERERERKRzkidaCxcuxMqVK/HLL79ALpdj9erV+PPPPzF06FC89NJLUndPRERERESkc5InWqmpqXjttdcAAHK5HPfu3YNMJsO0adPw1VdfSd09ERERERGRzkmeaNna2qKwsBAA0LhxY5w7dw4AkJeXh+LiYqm7JyIiIiIi0jnJF8Pw8/PDwYMH0a5dOwwZMgRTpkzBf/7zHxw8eBC9evWSunsiIiIiIiKdkzzRWrt2LUpKSgAAH3/8MYyNjXHs2DG8+eabmD17ttTdExERERER6ZxMEARB30HUZQUFBVAoFMjPz4e1tbW+wyEiIiIiIj2pTW4g+TNagYGBiImJQUFBgdRdERERERER1QmSJ1pt27ZFREQEHB0dMWTIEOzevRvl5eVSd0tERERERKQ3kidaq1evxs2bN7Fr1y5YWFhg5MiRcHBwwLhx4xAfHy9190RERERERDqn82e0SkpK8Msvv2DBggU4e/YsVCqVLruvNT6jRUREREREQO1yA8lXHXxUVlYWtm/fjm+//RZnzpxBly5ddNk9ERERERGRTkh+62BBQQGio6Px6quvwsXFBevXr8eAAQNw5coV/PHHH1J3T0REREREpHOSz2g5ODjA1tYWwcHBWLRoEby9vaXukoiIiIiISK8kT7T27NmDXr16wcBA8skzIiIiIiKiOkHy7OfVV1+FWq3G77//jo0bN6KwsBAAcOvWLRQVFUndPRERERERkc5JPqN1/fp1BAUFIT09HaWlpXj11VdhZWWFJUuWoLS0FBs2bJA6BCIiIiIiIp2SfEZrypQp8Pb2xt27d2FmZiaWDx48GHFxcVJ3T0REREREpHOSz2gdPnwYx44dg1wu1yh3dXXFzZs3pe6eiIiIiIhI5ySf0VKr1VW+lPjGjRuwsrKSunsiIiIiIiKdkzzR6t27N1atWiVuy2QyFBUVITIyEv369ZO6eyIiIiIiIp2TCYIgSNnBjRs30KdPHwiCgCtXrsDb2xtXrlyBvb09/vvf/6JRo0ZSdv/MCgoKoFAokJ+fD2tra32HQ0REREREelKb3EDyRAsAHjx4gO3bt+PMmTMoKipCx44dERISorE4Rl3FRIuIiIiIiIDa5QY6eYuwkZERhg8fjqVLl2LdunUYM2bMUydZX375JVxdXWFqagofHx8kJiZWW3fTpk3o3r07bG1tYWtri8DAwMfWJyIiIiIi0gZJVh3cs2cP+vbtC2NjY+zZs+exdQcMGFDjdnfs2IHw8HBs2LABPj4+WLVqFfr06YNLly5VeQuiUqnEsGHD0LVrV5iammLJkiXo3bs3zp8/j8aNG9f6vIiIiIiIiGpCklsHDQwMkJWVhUaNGsHAoPpJM5lMVuWKhNXx8fFB586dsXbtWgAVKxq6uLhg8uTJmDVr1hOPV6lUsLW1xdq1azFy5Mgq65SWlqK0tFTcLigogIuLC28dJCIiIiJ6wen91kG1Wi3OMKnV6mo/tUmyysrKcPLkSQQGBoplBgYGCAwMREJCQo3aKC4uRnl5Oezs7Kqts2jRIigUCvHj4uJS4xiJiIiIiIgAiZ/RKi8vR69evXDlypVnbisnJwcqlQoODg4a5Q4ODsjKyqpRGzNnzoSzs7NGsvZPERERyM/PFz8ZGRnPFDcREREREb14JHlG6yFjY2OcOXNGyi5qbPHixdi+fTuUSiVMTU2rrWdiYgITExMdRkZERERERM8byVcdHD58ODZv3vzM7djb28PQ0BDZ2dka5dnZ2XB0dHzsscuWLcPixYtx4MABeHp6PnMsREREREREjyPpjBZQ8Q6tqKgo/P777+jUqRMsLCw09q9YsaJG7cjlcnTq1AlxcXEYNGgQgIrnv+Li4jBp0qRqj1u6dCkWLFiA/fv3w9vb+6nPg4iIiIiIqKYkT7TOnTuHjh07AgAuX778TG2Fh4cjNDQU3t7e6NKlC1atWoV79+4hLCwMADBy5Eg0btwYixYtAgAsWbIEc+bMwb///W+4urqKz3JZWlrC0tLymWIhIiIiIiKqjuSJ1qFDh7TWVnBwMO7cuYM5c+YgKysLXl5eiI2NFRfISE9P11hOfv369SgrK8Nbb72l0U5kZCTmzp2rtbiIiIiIiIgeJcl7tB717rvvYvXq1bCystIov3fvHiZPnoyoqCgpu39mtVkrn4iIiIiInl96f4/Wo7Zs2YL79+9XKr9//z6++eYbqbsnIiIiIiLSOcluHSwoKIAgCBAEAYWFhRpLqqtUKuzbt098qTEREREREdHzRLJEy8bGBjKZDDKZDK1ataq0XyaTYd68eVJ1T0REREREpDeSJVqHDh2CIAjo2bMnfvrpJ9jZ2Yn75HI5mjZtCmdnZ6m6JyIiIiIi0hvJEi1/f38AQFpaGl566SXIZDKpuiIiIiIiIqpTJF8Mo2nTpjhy5AiGDx+Orl274ubNmwCArVu34siRI1J3T0REREREpHOSJ1o//fQT+vTpAzMzM5w6dQqlpaUAgPz8fCxcuFDq7omIiIiIiHRO8kRr/vz52LBhAzZt2gRjY2OxvFu3bjh16pTU3RMREREREemc5InWpUuX4OfnV6lcoVAgLy9P6u6JiIiIiIh0TvJEy9HREVevXq1UfuTIETRv3lzq7omIiIiIiHRO8kRr7NixmDJlCo4fPw6ZTIZbt25h27ZtmDFjBsaPHy9190RERERERDon2fLuD82aNQtqtRq9evVCcXEx/Pz8YGJighkzZmDy5MlSd09ERERERKRzMkEQBF10VFZWhqtXr6KoqAgeHh6wtLTE/fv3YWZmpovun1pBQQEUCgXy8/NhbW2t73CIiIiIiEhPapMbSH7r4ENyuRweHh7o0qULjI2NsWLFCjRr1kxX3RMREREREemMZIlWaWkpIiIi4O3tja5du2LXrl0AgOjoaDRr1gwrV67EtGnTpOqeiIiIiIhIbyR7RmvOnDnYuHEjAgMDcezYMQwZMgRhYWH4448/sGLFCgwZMgSGhoZSdU9ERERERKQ3kiVaP/zwA7755hsMGDAA586dg6enJx48eICUlBTIZDKpuiUiIiIiItI7yW4dvHHjBjp16gQAePnll2FiYoJp06YxySIiIiIioueeZImWSqWCXC4Xt42MjGBpaSlVd0RERERERHWGZLcOCoKAUaNGwcTEBABQUlKC9957DxYWFhr1du7cKVUIREREREREeiFZohUaGqqxPXz4cKm6IiIiIiIiqlMkS7Sio6OlapqIiIiIiKhO09kLi4mIiIiIiF4UTLSIiIiIiIi0jIkWERERERGRljHRIiIiIiIi0jImWkRERERERFrGRIuIiIiIiEjLmGgRERERERFpWb1LtL788ku4urrC1NQUPj4+SExMrLbu+fPn8eabb8LV1RUymQyrVq3SXaBERERERPTCqleJ1o4dOxAeHo7IyEicOnUK7du3R58+fXD79u0q6xcXF6N58+ZYvHgxHB0ddRwtERERERG9qGSCIAj6DqKmfHx80LlzZ6xduxYAoFar4eLigsmTJ2PWrFmPPdbV1RVTp07F1KlTH1uvtLQUpaWl4nZBQQFcXFyQn58Pa2vrZz4HIiIiIiKqnwoKCqBQKGqUG9SbGa2ysjKcPHkSgYGBYpmBgQECAwORkJCgtX4WLVoEhUIhflxcXLTWNhERERERvRjqTaKVk5MDlUoFBwcHjXIHBwdkZWVprZ+IiAjk5+eLn4yMDK21TURERERELwYjfQdQ15iYmMDExETfYRARERERUT1Wb2a07O3tYWhoiOzsbI3y7OxsLnRBRERERER1Sr1JtORyOTp16oS4uDixTK1WIy4uDr6+vnqMjIiIiIiISFO9unUwPDwcoaGh8Pb2RpcuXbBq1Srcu3cPYWFhAICRI0eicePGWLRoEYCKBTQuXLgg/nzz5k0kJyfD0tISLVu21Nt5EBERERHR861eJVrBwcG4c+cO5syZg6ysLHh5eSE2NlZcICM9PR0GBn9P0t26dQsdOnQQt5ctW4Zly5bB398fSqVS1+ETEREREdELol69R0sfarNWPhERERERPb+ey/doERERERER1RdMtIiIiIiIiLSMiRYREREREZGWMdEiIiIiIiLSMiZaREREREREWsZEi4iIiIiISMuYaBEREREREWkZEy0iIiIiIiItY6JFRERERESkZUy0iIiIiIiItIyJFhERERERkZYx0SIiIiIiItIyJlpERERERERaxkSLiIiIiIhIy5hoERERERERaRkTLSIiIiIiIi1jokVERERERKRlTLSIiIiIiIi0jIkWERERERGRljHRIiIiIiIi0jLJEq2YmBjY2Ng8sZ5MJsOuXbukCoOIiIiIiEjnJEu0goODcfnyZXF77ty58PLykqq7J6pLCV1AQACmTp1ab9olIiIiIqLaMZKqYTMzM5iZmUnVPBERERERUZ1VqxmtvXv3wsbGBiqVCgCQnJwMmUyGWbNmiXXGjBmD4cOHa9w6GBMT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",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in [\"without-context\", \"with-context\"]:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df01f761-d3a7-4783-9fa7-57f62679b548",
   "metadata": {},
   "source": [
    "**Sanity check**: the retrieval score for without-context is zero since we’re using any context.\n",
    "\n",
    "As we can see, using context (RAG) does indeed help in the quality of our answers (and by a meaningful margin)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6acc3a24-007d-4add-b0dd-5832351c6d63",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Chunk size"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11775f23-89ef-432d-a00d-2a11ffaebf67",
   "metadata": {},
   "source": [
    "Next, we'll access various chunk sizes. Smaller chunks (but not too small!) are able to encapsulate atomic concepts which yields more precise retrieval. While larger chunks may be more noisy. Popular strategies include using small chunks but retrieving a bit of the [surrounding chunks](https://gpt-index.readthedocs.io/en/latest/end_to_end_tutorials/dev_practices/production_rag.html#decoupling-chunks-used-for-retrieval-vs-chunks-used-for-synthesis) around it (since it may have relevnat info) or store [mulitple embeddings](https://python.langchain.com/docs/modules/data_connection/retrievers/multi_vector) per document (ex. summary embedding per document)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "64b691b8-b2ea-4f77-857a-09e50053699c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "chunk_sizes = [100, 300, 500, 700, 900]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53c054d1-9e4d-4688-b4b9-cf5a39b382c3",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica, you can follow these steps:\n\n1. First, you need to get the handle of the replica you want to kill. You can do this by using the `ray.get_actor()` function and providing the ACTOR\\_ID of the replica. In your case, the ACTOR\\_ID is \"SERVE\\_REPLICA::SleepyPid#RlRptP\".\n2. Once you have the replica handle, you can use the `ray.kill()` function to kill the replica. Set the `no_restart` parameter to `True` to prevent the replica from automatically restarting.\n\nHere's an example command to kill the specific replica:\n```python\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\nray.kill(replica_handle, no_restart=True)\n```\nNote that you can get the list of all actors and their corresponding ACTOR\\_IDs by running the command `ray list actors`.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer is detailed, accurate, and provides a step-by-step guide on how to kill a specific serve replica. It also includes a code snippet for better understanding. The reference answer does not provide any useful information, making the generated answer superior.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 5,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/kubernetes.html#next-steps"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [24:52<00:00,  8.43s/it]\n"
     ]
    }
   ],
   "source": [
    "for chunk_size in chunk_sizes:\n",
    "    experiment_name = f\"chunk-size-{chunk_size}\"\n",
    "    experiment_names.append(experiment_name)\n",
    "    run_experiment(\n",
    "        experiment_name=experiment_name, \n",
    "        chunk_size=chunk_size, \n",
    "        chunk_overlap=50, \n",
    "        num_chunks=5,\n",
    "        embedding_model_name=embedding_model_name,\n",
    "        embedding_dim=EMBEDDING_DIMENSIONS[embedding_model_name],\n",
    "        llm=llm,\n",
    "        evaluator=EVALUATOR,\n",
    "        docs_dir=DOCS_DIR, \n",
    "        experiments_dir=EXPERIMENTS_DIR, \n",
    "        references_fp=REFERENCES_FILE_PATH,\n",
    "        num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5155e8b6-5a52-4154-b05e-12af5a0e413d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "chunk-size-100\n",
      "  retrieval score: 0.4180790960451977\n",
      "  quality score: 3.288135593220339\n",
      "\n",
      "chunk-size-300\n",
      "  retrieval score: 0.5254237288135594\n",
      "  quality score: 3.531073446327684\n",
      "\n",
      "chunk-size-500\n",
      "  retrieval score: 0.5480225988700564\n",
      "  quality score: 3.6271186440677967\n",
      "\n",
      "chunk-size-700\n",
      "  retrieval score: 0.519774011299435\n",
      "  quality score: 3.76271186440678\n",
      "\n",
      "chunk-size-900\n",
      "  retrieval score: 0.5706214689265536\n",
      "  quality score: 3.6779661016949152\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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BuLy5cuIiIjAmjVrxHp8fX3Rp08frFq1Ch4eHti1axfOnj2LDRs2VOvzIyIiInrT6A0cCN3+/YtXP8zOhpqREbS7dmnwI14lqrXPl7a2dqnFEajmuFm4YXXf1WXu8xXoHFir+3zNnz8fampqCAoKwt27d2FmZoapU6dW6r329vZYt24dlixZgtDQUIwYMQL+/v5/K3no2bMnDh48iCFDhkBVVRWffvppqRgDAwPs27cPCxcuxPPnz2Fra4udO3eWuUjHyZMnIZfLMXXqVKXr8vb2RnR0NNzd3RETE4OQkBAsX74c6urqaNu2bYUjgZXZO05fXx9xcXGYPn06unTpgubNmyMoKEjc4wsoTjZ37NiBefPm4YsvvoCtrS3279/PPb6IiIiIXiJRVYVOd+f67ka1SIRXH9J5jaVLl8LExERcHa7Eli1bkJ2djcDAwBrtYEOVl5cHfX195ObmlhrZef78OdLT02FlZQWpVFrtNuQKOc7fP4/sp9kw0jaCk7FTrYx4UeNVU/caEREREVVfRbnBy6q84Mb69evLXNmuffv2iIqKqmp1VAFVFVV0M+2GIdZD0M20GxMvIiIiIqJGrMrJl0wmE5cYf5mRkRGysrJqpFNERERERERvmionX+bm5vjll19Knf/ll1/QokWLGukUERERERHRm6bKC274+Phg1qxZKCoqwnvvvQcASEhIQEBAAD777LMa7yAREREREdGboMrJ1+eff44HDx7gk08+QWFhIYC/liifO3dujXeQiIiIiIjoTVDl5EsikWD58uWYP38+rl69Ci0tLdja2kJTU7M2+kdERERERPRGqPIzXyWaNGmCbt26QVdXFzdu3IBCoajJfhEREREREb1RKp18bdmyBatXr1Y69/HHH8Pa2hodO3ZEhw4dcPv27RrvIBERERER0Zug0snXhg0bYGhoKL6OjY3F1q1b8e9//xtnzpyBgYEBgoODa6WTREREREREjV2lk69r166ha9eu4usDBw7g/fffx5gxY+Dk5IQlS5YgISGhVjpJ9S8jIwMSiQQpKSm13palpSXCw8NrrL6+ffti1qxZNVYfEREREVF1VDr5evbsGfT09MTXSUlJ6N27t/ja2toaMpmsZnv3lhPkcjw5dRq5MQfx5NRpCHJ5fXepUdq3bx9CQ0Nrrf5jx45BIpGUeZw5c0aMu3jxInr16gWp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gYEBJBIJJBIJ7OzsSpVLJBIEBwdXr7dERERERERvuEonX4mJiRAEAe+99x5++OEHNG3aVCzT0NCAhYUFWrRoUSudJCIiIiIiauwqnXz16dMHAJCeno5WrVpBIpHUWqeIiIiIiIjeNFVecMPCwgInT57E2LFj4erqij/++AMA8O233+LkyZM13kEiIiIiIqI3QZWTrx9++AHu7u7Q0tLC+fPnUVBQAADIzc3FkiVLaryDREREREREb4IqJ1+LFi1CVFQUNm7cCHV1dfF8jx49cP78+RrtHBERERER0ZuiyslXWloaevfuXeq8vr4+cnJyaqJPREREREREb5wqJ1+mpqa4fv16qfMnT56EtbV1jXSKiIiIiIjoTVPl5MvHxwe+vr44deoUJBIJ7t69i+3bt8Pf3x/Tpk2rjT4SERERERE1epVear7EnDlzoFAo0L9/fzx9+hS9e/eGpqYm/P398emnn9ZGH4mIiIiIiBo9iSAIQnXeWFhYiOvXryM/Px/t2rVDkyZN8OzZM2hpadV0HxukvLw86OvrIzc3F3p6evXdHSIiIiIiqieVzQ2qPO2whIaGBtq1awdnZ2eoq6tj9erVsLKyqm51REREREREb7RKJ18FBQWYO3cuunbtCldXV+zfvx8AsHXrVlhZWWHNmjWYPXt2bfWTiIiIiIioUav0M19BQUFYv3493NzckJSUhA8++AATJkzAr7/+itWrV+ODDz6AqqpqbfaViIiIiIio0ap08rV37178+9//xrBhw3D58mV06tQJL168wIULFyCRSGqzj0RERERERI1epacd3rlzB126dAEAdOjQAZqampg9ezYTLyIiIiIiokqodPIll8uhoaEhvlZTU0OTJk1qpVNERERERERvmkpPOxQEAePHj4empiYA4Pnz55g6dSp0dHSU4vbt21ezPSQiIiIiInoDVDr58vb2Vno9duzYGu8MERERERHRm6rSydfWrVtrsx9ERERERERvtGpvskxERERERESVx+SLiIiIiIioDjD5IiIiIiIiqgP1nnytXbsWlpaWkEql6N69O06fPl2p9+3atQsSiQTDhw9XOj9+/HhIJBKlY9CgQUoxDx8+xJgxY6CnpwcDAwNMmjQJ+fn5NXVJREREREREpdRr8rV79274+flhwYIFOH/+PBwcHODu7o779+9X+L6MjAz4+/ujV69e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NauPblMDxeTrLXH//n14eXkhKysL+vr66NSpE3766SdxAY3MzEyoqPw1EPrkyRN88sknuHPnDrS0tNC2bVt89913GDlyZH1dAjUgVb2fiF6nqvfUo0eP4OPjA5lMBkNDQ3Tp0gVJSUmVej6M3nxVvZ/Mzc3x008/Yfbs2ejUqRNatmwJX19fBAYG1tclUANSnf/npaWl4eTJk4iLi6uPLlMDxme+iIiIiIiI6gD/NE1ERERERFQHmHwRERERERHVASZfREREREREdYDJFxERERERUR1g8kVERERERFQHmHwRERERERHVASZfREREREREdYDJFxERERERUR1g8kVERG+thQsXonPnzuLr8ePHY/jw4fXWHyIierMx+SIiogbl9u3bmDhxIlq0aAENDQ1YWFjA19cXDx48qPW2IyIiEB0dLb7u27cvZs2a9bfrffr0KebOnYvWrVtDKpXCyMgIffr0wYEDB/523URE1Hio1XcHiIiISty8eRMuLi6ws7PDzp07YWVlhStXruDzzz/H4cOH8euvv6Jp06a11r6+vn6t1Dt16lScOnUKX3/9Ndq1a4cHDx4gKSmpVhPKwsJCaGho1Fr9RERUdRz5IiKiBmP69OnQ0NBAXFwc+vTpg1atWmHw4ME4cuQI/vjjD3z55ZdirEQiwf79+5Xeb2BgoDRyFRgYCDs7O2hra8Pa2hrz589HUVFRue2/PO1w/PjxOH78OCIiIiCRSCCRSJCeng4bGxusXLlS6X0pKSmQSCS4fv16mfX++OOP+OKLLzBkyBBYWlqiS5cu+PTTTzFx4kQxpqCgAIGBgTA3N4empiZsbGywefNmsfz48eNwdnaGpqYmzMzMMGfOHLx48UIs79u3L2bMmIFZs2ahefPmcHd3BwBcvnwZgwcPRpMmTWBiYoJx48bhzz//LPczICKi2sPki4iIGoSHDx/ip59+wieffAItLS2lMlNTU4wZMwa7d++GIAiVrlNXVxfR0dFITU1FREQENm7ciDVr1lTqvREREXBxcYGPjw+ysrKQlZWFVq1aYeLEidi6datS7NatW9G7d2/Y2NiUWZepqSkOHTqEx48fl9uel5cXdu7cia+++gpXr17F+vXr0aRJEwDAH3/8gSFDhqBbt264cOECvvnmG2zevBmLFi1SqmPbtm3Q0NDAL7/8gqioKOTk5OC9996Do6Mjzp49i9jYWNy7dw8ffvhhpT4DIiKqWZx2SEREDcK1a9cgCALs7e3LLLe3t8ejR4+QnZ0NY2PjStU5b9488WtLS0v4+/tj165dCAgIeO179fX1oaGhAW1tbZiamornx48fj6CgIJw+fRrOzs4oKirCjh07So2GvWzDhg0YM2YMmjVrBgcHB/Ts2ROenp7o0aMHAOD333/Hnj17EB8fDzc3NwCAtbW1+P5169bB3NwckZGRkEgkaNu2Le7evYvAwEAEBQVBRaX4b6m2trYICwsT37do0SI4OjpiyZIl4rktW7bA3Nwcv//+O+zs7F77ORARUc3hyBcRETUorxvZqspzTLt370aPHj1gamqKJk2aYN68ecjMzPxb/WvRogU8PDywZcsWAMB///tfFBQU4IMPPij3Pb1798bNmzeRkJAAT09PXLlyBb169UJoaCiA4mmLqqqq6NOnT5nvv3r1KlxcXCCRSMRzPXr0QH5+Pu7cuSOe69Kli9L7Lly4gMTERDRp0kQ82rZtCwC4ceNG9T4AIiKqNiZfRETUINjY2EAikeDq1atlll+9ehVGRkYwMDAAUPzM16uJ2svPcyUnJ2PMmDEYMmQIYmJi8Ntvv+HLL79EYWHh3+7r5MmTsWvXLjx79gxbt27FyJEjoa2tXeF71NXV0atXLwQGBiIuLg4hISEIDQ1FYWFhqWmW1aWjo6P0Oj8/H//4xz+QkpKidFy7dg29e/eukTaJiKjyOO2QiIgahGbNmmHAgAFYt24dZs+erZSQyGQybN++HdOnTxfPGRkZISsrS3x97do1PH36VHydlJQECwsLpUU6bt26VaU+aWhoQC6Xlzo/ZMgQ6Ojo4JtvvkFsbCx+/vnnKtULAO3atcOLFy/w/PlzdOzYEQqFAsePHxenHb7M3t4eP/zwAwRBEEe/fvnlF+jq6uKdd94ptw0nJyf88MMPsLS0hJoa/5dPRFTfOPJFREQNRmRkJAoKCuDu7o6ff/4Zt2/fRmxsLAYMGAA7OzsEBQWJse+99x4iIyPx22+/4ezZs5g6dSrU1dXFcltbW2RmZmLXrl24ceMGvvrqK/znP/+pUn8sLS1x6tQpZGRk4M8//4RCoQAAqKqqYvz48Zg7dy5sbW3h4uJSYT19+/bF+vXrce7cOWRkZODQoUP44osv0K9fP+jp6cHS0hLe3t6YOHEi9u/fj/T0dBw7dgx79uwBAHzyySe4ffs2Pv30U/zvf//DgQMHsGDBAvj5+YnPe5Vl+vTpePjwIUaNGoUzZ87gxo0b+OmnnzBhwoQyk0oiIqpdTL6IiKjBsLW1xZkzZ2BtbY0PP/wQFhYWGDx4MOzs7PDLL7+Iq/8BwKpVq2Bubo5evXph9OjR8Pf3V5r6N2zYMMyePRszZsxA586dkZSUhPnz51epP/7+/lBVVUW7du1gZGSk9LzYpEmTUFhYiAkTJry2Hnd3d2zbtg0DBw6Evb09Pv30U7i7u4vJFQB888038PT0xCeffIK2bdvCx8cHT548AQC0bNkShw4dwunTp+Hg4ICpU6di0qRJSguKlKVFixb45ZdfIJfLMXDgQHTs2BGzZs2CgYFBhUkbERHVDolQlTV7iYiI6tiCBQuwevVqxMfH4913363v7ohOnDiB/v374/bt2zAxManv7hARUSPA5IuIiBq8rVu3Ijc3FzNnzqz3EZuCggJkZ2fD29sbpqam2L59e732h4iIGg8mX0RERFUQHR2NSZMmoXPnzvjxxx/RsmXL+u4SERE1Eky+iIiIiIiI6gCftiUiIiIiIqoDTL6IiIiIiIjqAJMvIiIiIiKiOsDki4iIiIiIqA4w+SIiIiL6v/brWAAAAABgkL/1LHaVRQAD+QIAABjIFwAAwEC+AAAABgEzZpU3pesBVQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "680569cb-d1b3-464e-b771-1f5dd3d0cc66",
   "metadata": {},
   "source": [
    "It appears that larger chunk sizes do help but tapers off (too much context might be too noisy). Larger chunk sizes [aren’t always better](https://arxiv.org/abs/2307.03172).\n",
    "\n",
    "**Note**: If we were to use larger chunk sizes (ours is based on characters), keep in mind that [most](https://huggingface.co/spaces/mteb/leaderboard) open source embedding models have a maximum sequence length of 512 sub-word tokens. This means that if our chunk contains more than 512 sub-word tokens (4 chars ≈ 1 token), the embedding wouldn't account for it anyway (unless we finetune our embedding model to have longer sequence lengths)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04df6ec4-7edf-4a27-93ae-7ee2b3ff7241",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "CHUNK_SIZE = 700\n",
    "CHUNK_OVERLAP = 50"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc80dd05-ced6-49b4-a193-c52fcebc118e",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Number of chunks"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84fbb854-b016-4c42-97bc-56c7eebfa3dd",
   "metadata": {},
   "source": [
    "Next, we'll experiment with the number of chunks to use. More chunks will allow us to add more context but too many could potentially introduce a lot of noise.\n",
    "\n",
    "**Note**: The `chunk_size` we chose multiplied by the `num_chunks` below fits inside the LLM's context length. We're experimenting with the chunk size and number of chunks as if they were indepdent variables but they area heavily related. Especially since all of our LLMs have a finite maximum context length. So ideally, we would tune for a combination if `chunk_size` * `num_chunks`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d74ca9d-8f04-4e91-a927-87820acb1aed",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "num_chunks_list = [1, 3, 5, 7, 9, 11, 13, 15]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0136db87-bb7b-4d7f-ad7a-549c1e6fd489",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "num_chunks_list = [14]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f09d999-3e02-46c7-a48f-eb9be5edc37a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PosixPath('/home/ray/ray-assistant/notebooks/../experiments/references/gpt-4-turbo.json')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "REFERENCES_FILE_PATH"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c514d9c7-e16d-44c4-88b7-72a49b5c4197",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica in Ray, you can follow these steps:\n\n1. Get a list of actors in Ray, including the replicas, by running `ray list actors` in the Python interpreter.\n2. Identify the NAME of the replica you want to kill from the list.\n3. Get a handle to the replica by running `replica_handle = ray.get_actor(\"NAME\", namespace=\"serve\")` in the Python interpreter.\n4. Kill the replica by running `ray.kill(replica_handle, no_restart=True)` in the Python interpreter.\n\nFor example, if you have a replica with the NAME \"SERVE\\_REPLICA::SleepyPid#PHvdJW\", you can kill it by running:\n```makefile\n$ python\n>>> import ray\n>>> replica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#PHvdJW\", namespace=\"serve\")\n>>> ray.kill(replica_handle, no_restart=True)\n>>> exit()\n```",
       "question": "how do I kill a specific serve replica",
       "reasoning": "",
       "reference_answer": "The context provided does not contain the necessary information to answer the query.",
       "score": 4,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#head-node-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling",
        "https://docs.ray.io/en/master/serve/production-guide/kubernetes.html#next-steps",
        "https://docs.ray.io/en/master/serve/production-guide/config.html#serve-config-files-serve-build",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#replica-health-checking",
        "https://docs.ray.io/en/master/serve/architecture.html#ray-serve-autoscaling"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [21:21<00:00,  7.24s/it]\n"
     ]
    }
   ],
   "source": [
    "for num_chunks in num_chunks_list:\n",
    "    experiment_name = f\"num-chunks-{num_chunks}\"\n",
    "    experiment_names.append(experiment_name)\n",
    "    run_experiment(\n",
    "        experiment_name=experiment_name,\n",
    "        chunk_size=CHUNK_SIZE, \n",
    "        chunk_overlap=CHUNK_OVERLAP, \n",
    "        num_chunks=num_chunks,\n",
    "        embedding_model_name=embedding_model_name,\n",
    "        embedding_dim=EMBEDDING_DIMENSIONS[embedding_model_name],\n",
    "        llm=llm,\n",
    "        evaluator=EVALUATOR,\n",
    "        docs_dir=DOCS_DIR, \n",
    "        experiments_dir=EXPERIMENTS_DIR, \n",
    "        references_fp=REFERENCES_FILE_PATH,\n",
    "        num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d688ed6b-2e32-4c80-b83f-e5ebbca88ea6",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "num-chunks-1\n",
      "  retrieval score: 0.2542372881355932\n",
      "  quality score: 3.4237288135593222\n",
      "\n",
      "num-chunks-3\n",
      "  retrieval score: 0.4689265536723164\n",
      "  quality score: 3.6299435028248586\n",
      "\n",
      "num-chunks-5\n",
      "  retrieval score: 0.519774011299435\n",
      "  quality score: 3.709039548022599\n",
      "\n",
      "num-chunks-7\n",
      "  retrieval score: 0.6271186440677966\n",
      "  quality score: 3.7937853107344632\n",
      "\n",
      "num-chunks-9\n",
      "  retrieval score: 0.6836158192090396\n",
      "  quality score: 3.8983050847457625\n",
      "\n",
      "num-chunks-11\n",
      "  retrieval score: 0.7175141242937854\n",
      "  quality score: 3.9180790960451977\n",
      "\n",
      "num-chunks-13\n",
      "  retrieval score: 0.7570621468926554\n",
      "  quality score: 3.983050847457627\n",
      "\n",
      "num-chunks-15\n",
      "  retrieval score: 0.7740112994350282\n",
      "  quality score: 3.983050847457627\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee986a80-8659-4344-bb22-71bb62b32946",
   "metadata": {},
   "source": [
    "Increasing our number of chunks improves our retrieval and quality scores up to a certain point. However, for some models (ex. `llama-2`), the context length is much smaller so we won't be able to use as many chunks. For all of these models, it's worth investing in extending context size via RoPE scaling (rotary position embeddings), etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fbe4828f-9639-4957-898e-27dd0ce3ee32",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "NUM_CHUNKS = 13"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a04dff3-5323-419f-a290-849c96899292",
   "metadata": {},
   "source": [
    "### Embedding models"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df10c471-22b5-479c-bbbd-59ff3835d7b9",
   "metadata": {},
   "source": [
    "So far, we've used [thenlper/gte-base](https://huggingface.co/thenlper/gte-base) as our embedding model because it's a relatively small (0.22 GB) and performant option. But now, let's explore other popular options such as the current leader on the [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard), [BAAI/bge-large-en](https://huggingface.co/BAAI/bge-large-en) (1.34 GB), [thenlper/gte-large](https://huggingface.co/thenlper/gte-large) (a larger version of `gte-base`), and OpenAI's [text-embedding-ada-002](https://openai.com/blog/new-and-improved-embedding-model)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "198ec597-8aaf-4c45-a275-2094211eebb4",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "embedding_model_names = [\"thenlper/gte-base\", \"thenlper/gte-large\", \"BAAI/bge-large-en\", \"text-embedding-ada-002\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1913f50c-ef13-487d-beeb-77ee38f91067",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": "  To kill a specific serve replica, you can use the `ray kill` command with the actor ID of the replica. You can get the actor ID by running `ray list actors` and filtering the output by the class name of the replica. For example, if you want to kill a replica with the class name `ServeReplica:SleepyPid`, you can run `ray kill <actor_id>`.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer is detailed and provides a step-by-step guide on how to kill a specific serve replica, which is exactly what the question asked for. The reference answer does not provide any useful information, so the generated answer is much better in this case.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 5,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/ray-observability/getting-started.html#serve-application-detail-page",
        "https://docs.ray.io/en/master/cluster/kubernetes/troubleshooting/rayservice-troubleshooting.html#method-5-ray-state-cli",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/monitoring.html#ray-dashboard",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [20:57<00:00,  7.10s/it]\n"
     ]
    }
   ],
   "source": [
    "for embedding_model_name in embedding_model_names:\n",
    "    experiment_name = f\"{embedding_model_name.split('/')[-1]}\"\n",
    "    experiment_names.append(experiment_name)\n",
    "    run_experiment(\n",
    "        experiment_name=experiment_name, \n",
    "        chunk_size=CHUNK_SIZE, \n",
    "        chunk_overlap=CHUNK_OVERLAP, \n",
    "        num_chunks=NUM_CHUNKS,\n",
    "        embedding_model_name=embedding_model_name,\n",
    "        embedding_dim=EMBEDDING_DIMENSIONS[embedding_model_name],\n",
    "        llm=llm,\n",
    "        evaluator=EVALUATOR,\n",
    "        docs_dir=DOCS_DIR, \n",
    "        experiments_dir=EXPERIMENTS_DIR, \n",
    "        references_fp=REFERENCES_FILE_PATH,\n",
    "        num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60ec4e25-b11c-4ee1-a9a9-ce29eb6dc81e",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gte-base\n",
      "  retrieval score: 0.7570621468926554\n",
      "  quality score: 3.9322033898305087\n",
      "\n",
      "gte-large\n",
      "  retrieval score: 0.7796610169491526\n",
      "  quality score: 3.9350282485875705\n",
      "\n",
      "bge-large-en\n",
      "  retrieval score: 0.4745762711864407\n",
      "  quality score: 3.480225988700565\n",
      "\n",
      "text-embedding-ada-002\n",
      "  retrieval score: 0.6497175141242938\n",
      "  quality score: 3.5395480225988702\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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srKyntk+NjYW58+fx+jRo5XKvby8sGHDBuzfvx/z58/HwYMH0adPn3JvqH6ksLAQubm5ShsREREREdUMrzYWWDnCBeYGylP/zA20Xqpl2FVV589cvYi1a9fC0dERHTp0UCofOnSo9LWjoyOcnJzQvHlzREVFoWfPnuX6CQ0NxezZs2s8XiIiIiIiKuPVxgK9HMwRm3YHN+4XwFSvbCpgfRyxeqROR66MjY0hl8uRnZ2tVJ6dnQ1zc/Onts3Pz0dERESVXpDWrFkzGBsbIyUlpcLjU6dORU5OjrRduXKl6hdBRERERETPRa4mg1vzRvB2bgy35o3qdWIF1HFypaGhAVdXV+zfv18qUygU2L9/P9zc3J7a9rfffkNhYSFGjBjxzPNcvXoVt2/fhoVFxcOLmpqa0NfXV9qIiIiIiIhUUeerBQYHB2PNmjVYv349EhMTMW7cOOTn58Pf3x8A4Ovri6lTp5Zrt3btWgwYMACNGjVSKs/Ly8Pnn3+OY8eO4fLly9i/fz+8vb3RokULeHp61so1ERERERHR66fOn7n68MMPcfPmTcycORNZWVlwdnbGrl27pEUuMjIyoKamnAMmJSXhyJEj2LNnT7n+5HI5zp49i/Xr1+PevXuwtLRE7969MXfuXL7rioiIiIiIaoxMCCHqOoiXTW5uLgwMDJCTk8MpgkRERERErzFVcoM6nxZIRERERET0KmByRUREREREVA2YXBEREREREVUDJldERERERETVgMkVERERERFRNWByRUREREREVA2YXBEREREREVUDJldERERERETVgMkVERERERFRNWByRUREREREVA2YXBEREREREVUDJldERERERETVgMkVERERERFRNWByRUREREREVA2YXBEREREREVUDJldERERERETVgMkVERERERFRNWByRUREREREVA2YXBEREREREVUDJldERERERETVgMkVERERERFRNWByRUREREREVA2YXBEREREREVUDJldERERERETV4KVIrn744QfY2tpCS0sLHTt2RGxsbKV13d3dIZPJym3vvvuuVEcIgZkzZ8LCwgLa2trw8PDAxYsXa+NSiIiIiIjoNVXnyVVkZCSCg4Mxa9YsnDx5Em3btoWnpydu3LhRYf3NmzcjMzNT2s6fPw+5XI7BgwdLdb777jssX74cq1atwvHjx6GjowNPT08U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dR0G0StPXV39qSNW9fI9V1SeXE2O9ubt6zoMIiIiqgNyufyFpygRUe3ighZERERERETVgMkVERERERFRNWByRUREREREVA34zFUFHq3xkZubW8eREBERERFRXXqUE1RlHUAmVxW4f/8+AMDKyqqOIyEiIiIiopfB/fv3YWBg8NQ6XIq9AgqFAtevX4eent5r/eK+3NxcWFlZ4cqVK3W6JD29+nivUW3hvUa1hfca1RbeazVPCIH79+/D0tISampPf6qKI1cVUFNTQ5MmTeo6jJeGvr4+f1ipVvBeo9rCe41qC+81qi2812rWs0asHuGCFkRERERERNWAyRUREREREVE1YHJFldLU1MSsWbOgqalZ16HQK473GtUW3mtUW3ivUW3hvfZy4YIWRERERERE1YAjV0RERERERNWAyRUREREREVE1YHJFRERERERUDZhcERERERERVQMmV6+plStXwsnJSXrhnJubG3bu3Flp/fDwcMhkMqVNS0urFiOm+krVew0A7t27h/Hjx8PCwgKampqws7PDjh07ailiqq9Uvdfc3d3L/bsmk8nw7rvv1mLUVB89z79rS5cuRatWraCtrQ0rKysEBQWhoKCgliKm+krVe624uBhz5sxB8+bNoaWlhbZt22LXrl21GDE1qOsAqG40adIE3377LVq2bAkhBNavXw9vb2+cOnUKb775ZoVt9PX1kZSUJO3LZLLaCpfqMVXvtaKiIvTq1Qumpqb4/fff0bhxY6Snp8PQ0LD2g6d6RdV7bfPmzSgqKpL2b9++jbZt22Lw4MG1GTbVQ6rea7/88gumTJmCdevWoXPnzkhOTsbIkSMhk8mwePHiOrgCqi9UvdemT5+O//znP1izZg1at26N3bt3Y+DAgYiJiUG7du3q4ApeQ4Lo/73xxhvip59+qvBYWFiYMDAwqN2A6JX1tHtt5cqVolmzZqKoqKiWo6JX0dPutSctWbJE6Onpiby8vBqOil5FT7vXxo8fL9555x2lsuDgYNGlS5faCI1eMU+71ywsLMSKFSuUyt5//33h4+NTG6GREILTAgmlpaWIiIhAfn4+3NzcKq2Xl5cHGxsbWFlZwdvbGxcuXKjFKOlVUJV7bdu2bXBzc8P48eNhZmaGNm3aYN68eSgtLa3laKk+q+q/a49bu3Ythg4dCh0dnRqOjl4lVbnXOnfujPj4eMTG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nTt3MGzYMMTFxSE1NRW7d++Gv79/hUkjERHVPCZXRERUK1q2bIm4uDg0a9YMQ4YMgY2NDfr06QM7OztER0dLq+cBwKJFi2BlZYVu3bph+PDh+Oyzz5Sm5vXv3x9BQUEIDAyEs7MzYmJiMGPGDJXi+eyzzyCXy+Hg4AATExOl57VGjRqFoqIi+Pv7P7MfT09PrF+/Hr1794a9vT0mTJgAT09PKXkCgJUrV+KDDz7Ap59+itatWyMgIAD5+fkAgMaNG2PHjh2IjY1F27ZtMXbsWIwaNUppwY6KWFpaIjo6GqWlpejduzccHR0xefJkGBoaPjUpIyKimiMTqqx7S0REVI1mzZqFxYsXY+/evejUqVNdhyM5fPgwevbsiStXrsDMzKyuwyEionqCyRUREdWpsLAw5OTkYOLEiXU+4lJYWIibN2/Cz88P5ubm2LhxY53GQ0RE9QuTKyIiov8XHh6OUaNGwdnZGdu2bUPjxo3rOiQiIqpHmFwRERERERFVAz7xSkREREREVA2YXBEREREREVUDJldERERERETVgMkVERERERFRNWByRUREREREVA2YXBEREREREVUDJldERERERETVgMkVERERERFRNWByRUREREREVA3+DymIK8zer/3jAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7fd24b35-1db3-4326-ab0c-c4b484fb5aea",
   "metadata": {},
   "source": [
    "This is an interesting outcome because the #1 (`BAAI/bge-large-en`) on the current [leaderboard](https://huggingface.co/spaces/mteb/leaderboard) isn't necessarily the best for our specific task. Using the smaller `thenlper/gte-large` produced the best retrieval and quality scores in our experiments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "845ad771-65e1-44cf-813f-3aa167c07e31",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "EMBEDDING_MODEL_NAME = \"thenlper/gte-large\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b21b32f-bacb-4703-b16c-d4a7014779dc",
   "metadata": {},
   "source": [
    "### OSS vs. closed LLMs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f393785f-17da-45eb-bf69-1483f74a370e",
   "metadata": {},
   "source": [
    "We're now going to use the best configurations from above to evaluate different choices for the main LLM.\n",
    "\n",
    "**Note**:\n",
    "- We've been using a specific LLM so far to decide on the configuration so that specific LLM's performance here will be a bit biased.\n",
    "- This list is not exhaustive and even for the LLMs we use, there are versions with longer context windows available."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a91c87cb-ba0d-4044-9616-b2cbad239587",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "llms = [\"gpt-3.5-turbo\",\n",
    "        \"gpt-4\",\n",
    "        \"gpt-4-1106-preview\",\n",
    "        \"meta-llama/Llama-2-7b-chat-hf\", \n",
    "        \"meta-llama/Llama-2-13b-chat-hf\", \n",
    "        \"meta-llama/Llama-2-70b-chat-hf\",\n",
    "        \"codellama/CodeLlama-34b-Instruct-hf\",\n",
    "        \"mistralai/Mistral-7B-Instruct-v0.1\",\n",
    "        \"mistralai/Mixtral-8x7B-Instruct-v0.1\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a60c0aed-86da-4529-96be-f10088260363",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Some LLMs have much shorter context lengths (ex Llama-2 has 4096) so we'll restrict the number of chunks to a minimum.\n",
    "minimum_num_chunks = 9"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "072e8114-23ba-402e-a03d-594089e9b4d5",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": "To kill a specific serve replica in Ray Serve, you can follow these steps:\n\n1. Obtain a handle to the replica actor using `ray.get_actor()` with the replica's unique name and the namespace it belongs to.\n2. Use `ray.kill()` to kill the replica actor, passing the handle obtained in the previous step. You can set `no_restart=True` if you do not want the replica to be automatically restarted.\n\nHere is an example code snippet based on the context provided:\n\n```python\nimport ray\n\n# Get a handle to the specific serve replica actor\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\n\n# Kill the replica actor without restarting it\nray.kill(replica_handle, no_restart=True)\n```\n\nAfter running this code, the specific serve replica will be killed, and if `no_restart` is set to `True`, it will not be restarted. Other replicas will continue processing requests.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer provides a detailed and technically accurate response to the query, including a step-by-step guide and a code snippet. However, it does not perfectly match the reference answer, which states that the context does not provide information on how to kill a specific serve replica. Despite this, the generated answer is still highly informative and useful.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 4,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-deployments",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#worker-node-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-applications"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [13:18<00:00,  4.51s/it]\n"
     ]
    }
   ],
   "source": [
    "for llm in llms:\n",
    "    experiment_name = f\"{llm.split('/')[-1].lower()}\"\n",
    "    experiment_names.append(experiment_name)\n",
    "    run_experiment(\n",
    "        experiment_name=experiment_name, \n",
    "        chunk_size=CHUNK_SIZE, \n",
    "        chunk_overlap=CHUNK_OVERLAP, \n",
    "        num_chunks=minimum_num_chunks,\n",
    "        embedding_model_name=EMBEDDING_MODEL_NAME,\n",
    "        embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "        llm=llm,\n",
    "        evaluator=EVALUATOR,\n",
    "        docs_dir=DOCS_DIR, \n",
    "        experiments_dir=EXPERIMENTS_DIR, \n",
    "        references_fp=REFERENCES_FILE_PATH,\n",
    "        num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a8afbfe1-b129-408a-8ce0-8f321c585174",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gpt-3.5-turbo\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.559322033898305\n",
      "\n",
      "gpt-4\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.8728813559322033\n",
      "\n",
      "gpt-4-1106-preview\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 4.209039548022599\n",
      "\n",
      "llama-2-7b-chat-hf\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.2966101694915255\n",
      "\n",
      "llama-2-13b-chat-hf\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.4152542372881354\n",
      "\n",
      "llama-2-70b-chat-hf\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.598870056497175\n",
      "\n",
      "codellama-34b-instruct-hf\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.593220338983051\n",
      "\n",
      "mistral-7b-instruct-v0.1\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.440677966101695\n",
      "\n",
      "mixtral-8x7b-instruct-v0.1\n",
      "  retrieval score: 0.7288135593220338\n",
      "  quality score: 3.943502824858757\n",
      "\n"
     ]
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9add6de4-bc41-4f94-ba5a-edc3d81309a4",
   "metadata": {
    "tags": []
   },
   "source": [
    "**Sanity check**: the retrieval scores are all the same because the LLM we choose doesn’t impact that part of our application."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97d1cd68-f77a-4f13-9454-3add1fc65158",
   "metadata": {},
   "source": [
    "`mixtral-8x7b-instruct-v0.1` outperforms the other OSS LLMs and even the current `gpt-4` (currently 0613) and not too far behind `gpt-4-turbo` (currently 1106-preview)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0275cb71-6876-404a-bdbe-f79347162696",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "LLM = \"mistralai/Mixtral-8x7B-Instruct-v0.1\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ab5aca0-212b-4e82-97af-1ed95130ebc6",
   "metadata": {},
   "source": [
    "**Note**: Some of our LLMs have much larger context lengths, ex. `gpt-4` is 8192 tokens and `gpt-3.5-turbo-16k` is 16,384 tokens. We could increase the number of chunks that we use for these since we saw that increasing `num_chunks` continued to improve the retrieval and quality scores. However, we will keep this value fixed for now since the performance started to taper off anyway and so we can compare these performances under the exact same configurations. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4b0a5cc-15e7-486b-86d2-4165841da03a",
   "metadata": {},
   "source": [
    "### MoEs without context"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09ff5ffe-dce4-4bc6-93db-f4c924c23100",
   "metadata": {
    "tags": []
   },
   "source": [
    "Curious how well these mixture of experts (MoE) fare without any context."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7d0b694d-fd77-4b2b-8c6d-55f46307aea3",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "moes = [\"gpt-4\",\n",
    "        \"gpt-4-1106-preview\",\n",
    "        \"mistralai/Mixtral-8x7B-Instruct-v0.1\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6bcf4b91-c5a3-4465-b427-5b2720f72f69",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " I'm sorry for the confusion, but I need more context to provide a helpful answer. However, if you're asking about killing a specific replica of a service in a general sense (not tied to a specific technology), you might want to follow these steps:\n\n1. Identify the replica you want to kill, based on its process ID (PID) or some other unique identifier.\n2. Use the `kill` command followed by the PID or identifier to stop the replica. For example, `kill -9 PID` will forcefully terminate the process.\n\nPlease note that this is a general approach, and the specific steps might vary depending on the technology or environment you're using. If you could provide more context, I'd be happy to give a more tailored answer.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer provides a general approach to killing a specific service replica, which is useful even though the context does not provide specific information. However, it does not perfectly match the reference answer, hence the score is not a perfect 5.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 4,
       "sources": []
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [12:13<00:00,  4.14s/it]\n"
     ]
    }
   ],
   "source": [
    "for moe in moes:\n",
    "    experiment_name = f\"without-context-{moe.split('/')[-1].lower()}\"\n",
    "    experiment_names.append(experiment_name)\n",
    "    run_experiment(\n",
    "        experiment_name=experiment_name, \n",
    "        chunk_size=CHUNK_SIZE, \n",
    "        chunk_overlap=CHUNK_OVERLAP, \n",
    "        num_chunks=0,  # no retrieved context\n",
    "        embedding_model_name=EMBEDDING_MODEL_NAME,\n",
    "        embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "        llm=moe,\n",
    "        evaluator=EVALUATOR,\n",
    "        docs_dir=DOCS_DIR, \n",
    "        experiments_dir=EXPERIMENTS_DIR, \n",
    "        references_fp=REFERENCES_FILE_PATH,\n",
    "        num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "955176f4-dde5-4a2b-ad28-b538e30de118",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "without-context-gpt-4\n",
      "  retrieval score: 0.0\n",
      "  quality score: 1.4887005649717515\n",
      "\n",
      "without-context-gpt-4-1106-preview\n",
      "  retrieval score: 0.0\n",
      "  quality score: 3.8163841807909606\n",
      "\n",
      "without-context-mixtral-8x7b-instruct-v0.1\n",
      "  retrieval score: 0.0\n",
      "  quality score: 3.189265536723164\n",
      "\n"
     ]
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16420a7d-445a-48da-a4c1-5238c4061f6e",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Fine-tuning"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "285c9d07-1658-4779-9e13-801c0c77186a",
   "metadata": {},
   "source": [
    "Everything we have explored so far involves optimizing for how our data is preprocessed and using our models (embedding, LLM, etc.) as is. However, it's also worth exploring fine-tuning our models with data unique to our use case. This could help us better represent our data and ultimately increase our retrieval and quality scores. In this section, we're going to fine-tune our embedding model. The intuition here is that it may be worth it to learn a more contextual representation of our tokens than the default embedding models can. This can especially be impactful if we have a lot of:\n",
    "- new tokens that the default tokenization process creates subtokens out of that lose the significance of the token\n",
    "- existing tokens that have contextually different meanings in our use case\n",
    "\n",
    "<img width=\"800\" src=\"https://images.ctfassets.net/xjan103pcp94/4G5324lsDZwq0jES7uBH0l/a715cd50af7061e1b3c57ec3e8038f05/rag-based-llm-applications-finetune-embeddings.png\">\n",
    "\n",
    "When it comes to fine-tuning our embedding model, we will exploring two approaches:\n",
    "- **full parameter**: including the embedding layer and all subsequent encoder layers (transformer blocks)\n",
    "- **embedding layer**: to better represent our unique subtokens and avoid overfitting (version of linear adapter)\n",
    "\n",
    "**Note**: we will not be exploring fine-tuning our LLM in this section because our previous [experiments](https://www.anyscale.com/blog/fine-tuning-llama-2-a-comprehensive-case-study-for-tailoring-models-to-unique-applications) ([LoRa vs. full parameter](https://www.anyscale.com/blog/fine-tuning-llms-lora-or-full-parameter-an-in-depth-analysis-with-llama-2)) have shown that fine-tuning has helped tremendously with [form not facts](https://www.anyscale.com/blog/fine-tuning-is-for-form-not-facts), which in our case won't help too much (compared to for ex. SQL generation). However, your use cases might benefit from fine-tuning, so be sure to check out our [Anyscale Endpoints fine-tuning](https://www.anyscale.com/endpoints) to easily tune and serve models (fully hosted or private on your cloud)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56747523-2ef7-489e-bba3-089a29ca25c5",
   "metadata": {},
   "source": [
    "## Synthetic dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3f35bef-5f68-4483-86d9-413ec4d5ea8f",
   "metadata": {},
   "source": [
    "Our first step will be to create a dataset to fine-tune our embedding model on. Our current embedding models have been trained via self-supervised learning (word2vec, GloVe, next/masked token prediction, etc.) and so we will continue fine-tuning with a self-supervised workflow. We're going to reuse a very similar approach as our cold start QA dataset section earlier so that we can map sections in our data to questions. The fine-tuning task here will be for the model to determine which sections in our dataset maps best to the input query. This optimization task will allow our embedding model to learn better representations of tokens in our dataset.\n",
    "\n",
    "**Note**: While we could create a dataset mapping section titles with section text, we are creating a synthetic Q&A dataset because it will be most representative of the types of data we want to learn how to embed."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5bb8c5c1-3bab-4539-9b79-cb9931a57c15",
   "metadata": {},
   "source": [
    "Our prompt is going to be a bit different because we want to generate a variety of different questions and we're going to use `llama-70b` here so that we can scale this QA generation process (and avoid any rate limits). To be thorough, we're going to generate one question from every section in our dataset so that we can try to capture as many unique tokens as possible."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4de030ee-22bc-45ba-a6ad-2f82f6e04434",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "system_content = f\"\"\"\n",
    "Create one question using only the context provided starting with \"What\", \"How\" or \"Why\".\n",
    "Only respond with the question, don't say anything else (unecessary starting words, hints, etc.)\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0224a7ef-25ab-4d81-a6e8-a1532822e0d7",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Generate questions\n",
    "embedding_qa = []\n",
    "sections = sections_ds.take_all()\n",
    "max_context_length = int(0.5*MAX_CONTEXT_LENGTHS[LLM]-get_num_tokens(system_content))\n",
    "for section in tqdm(sections):\n",
    "    user_content = trim(\n",
    "        text=f\"context: {section['text']}\", \n",
    "        max_context_length=max_context_length)\n",
    "    response = generate_response(\n",
    "        llm=\"meta-llama/Llama-2-70b-chat-hf\",\n",
    "        temperature=0.0,\n",
    "        stream=False,\n",
    "        system_content=system_content,\n",
    "        user_content=user_content,\n",
    "        max_retries=1)\n",
    "    if response:\n",
    "        embedding_qa.append({\"question\": response, \"source\": section[\"source\"]})\n",
    "print (len(embedding_qa))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65ed5a1c-a55f-426d-a839-04385bd9268d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Path\n",
    "EMBEDDING_QA_FILE_PATH = Path(ROOT_DIR, \"datasets\", \"embedding_qa.json\")\n",
    "EMBEDDING_QA_FILE_PATH.parent.mkdir(parents=True, exist_ok=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98298e4e-b681-45eb-8ea4-37d66cdf8685",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Save to file\n",
    "with open(EMBEDDING_QA_FILE_PATH, \"w\") as fp:\n",
    "    json.dump(embedding_qa, fp, indent=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62ecc4c4-79da-4b9c-a9c3-7cdfb53615c3",
   "metadata": {},
   "source": [
    "## Training data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3826c940-a40b-42ea-a50e-0297c78b6929",
   "metadata": {},
   "source": [
    "We're now going to split our dataset into training and validation splits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c4a7e67-6f17-4607-ae27-ab3253c7ae33",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sentence_transformers import InputExample"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba0b81ee-20eb-4721-af49-64544c36dc26",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Load from file\n",
    "with open(EMBEDDING_QA_FILE_PATH, \"r\") as fp:\n",
    "    embedding_qa = json.load(fp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c7fadbbf-28ba-4fcc-baa2-e9df78af3c67",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Split counts\n",
    "num_train_samples = int(len(embedding_qa)*0.8)\n",
    "emb_qa_train = embedding_qa[:num_train_samples]\n",
    "emb_qa_val = embedding_qa[num_train_samples:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4c6cad8-8859-4710-a01f-8aff5e8b923b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 4581/4581 [03:44<00:00, 20.40it/s]\n"
     ]
    }
   ],
   "source": [
    "# Training dataset\n",
    "train_dataset = []\n",
    "for item in tqdm(emb_qa_train):\n",
    "    query = item[\"question\"]\n",
    "    source_text = fetch_text(item[\"source\"])\n",
    "    example = InputExample(texts=[query, source_text])\n",
    "    train_dataset.append(example)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfebe1c5-8a76-41df-9c16-aa9757e8e553",
   "metadata": {},
   "source": [
    "## Validation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cf33abf-6031-4829-8121-8dbde6b84570",
   "metadata": {},
   "source": [
    "Our validation evaluation criteria involves an information retrieval (IR) evaluator that will retrieve the top k similar documents from the corpus for each query. The [InformationRetrievalEvaluator](https://www.sbert.net/docs/package_reference/evaluation.html#sentence_transformers.evaluation.InformationRetrievalEvaluator) requires the following inputs:\n",
    "\n",
    "- queries: `Dict[str, str]`  #  qid => query\n",
    "- corpus: `Dict[str, str]`  #  cid => doc\n",
    "- relevant_docs: `Dict[str, Set[str]]`  #  qid => Set[cid]\n",
    "\n",
    "**Note**: While our dataset may have multiple valid sections for a particular query, we will treat all other sections besides the one used to generate the query, as negative samples. This isn't an ideal scenario but the noise introduced is minimal, especially since we are using this to tune a representation layer (and not for a classification task)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fccb9bc9-bd51-4289-9576-c822a7d85ca6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sentence_transformers.evaluation import InformationRetrievalEvaluator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "678540b5-4f1c-441b-8a64-fb1860fd6c55",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 1146/1146 [01:31<00:00, 12.47it/s]\n"
     ]
    }
   ],
   "source": [
    "# Validation dataset\n",
    "queries, corpus, relevant_docs = {}, {}, {}\n",
    "for i, item in tqdm(enumerate(emb_qa_val), total=len(emb_qa_val)):\n",
    "    queries[f\"qid_{i}\"] = item[\"question\"]\n",
    "    corpus[f\"cid_{i}\"] = fetch_text(item[\"source\"])\n",
    "    relevant_docs[f\"qid_{i}\"] = set([f\"cid_{i}\"])\n",
    "evaluator = InformationRetrievalEvaluator(queries, corpus, relevant_docs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e0f8ada-a80d-480a-be2f-90a54e4a2f37",
   "metadata": {},
   "source": [
    "We'll be using [MultipleNegativesRankingLoss](https://www.sbert.net/docs/package_reference/losses.html#multiplenegativesrankingloss) as our loss function. It will use the data points (`InputExample(texts=[query, source_text])` in our training data as positive pairs and all other combinations as negative pairs. And the objective will be to increase the cosine similarity (default `similarity_fct`) for our positive pair and decrease it for the other pairs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9810f112-b3f5-4767-ba8b-d4b556b51db8",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Custom callback to view validation performance\n",
    "def val_callback(score, epoch, steps):\n",
    "    print (f\"EPOCH: {epoch}, VAL SCORE:{score:.4f}\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57a1d156-c6b6-4488-afd7-ec7fe6add16b",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Embedding model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dfcf2c7d-20b0-4363-b8e5-a0b314a9c95f",
   "metadata": {},
   "source": [
    "Now we're ready to initialize our embedding model for fine-tuning."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0141b57e-72e7-42f4-8ab9-9ebd1c401de2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sentence_transformers import SentenceTransformer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3291d4e9-aed7-44f3-9742-6270682e9025",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "32dd444af3f1402dbcf6daea5e405744",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading .gitattributes:   0%|          | 0.00/1.52k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "9d0efc4247e741fb8effa7073c8bae21",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading 1_Pooling/config.json:   0%|          | 0.00/191 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "6d2153ba02c94f0eaaaf9034ea394568",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading README.md:   0%|          | 0.00/67.9k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ee9b41bbfc4343c48ebdd05e51b6cb57",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading config.json:   0%|          | 0.00/619 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1bb6ee29368f47148f91343d9ac4185a",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading model.safetensors:   0%|          | 0.00/670M [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "66467ecf19ea42bea6224261fd2d6845",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading onnx/config.json:   0%|          | 0.00/632 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "4c188d54748a4713b4ee5f8807819aaa",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading model.onnx:   0%|          | 0.00/1.34G [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ce84db913b0c469ea00412d7c9305913",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)cial_tokens_map.json:   0%|          | 0.00/125 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f09ef9b2be7f44c38f0b3488e4b42cbf",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading onnx/tokenizer.json:   0%|          | 0.00/712k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "212f898a4db2400b8dab7e50a801a478",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)okenizer_config.json:   0%|          | 0.00/342 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "27044bdd83d9413c9b15a0bb3297f0d5",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading onnx/vocab.txt:   0%|          | 0.00/232k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "807c16faf8c9458490527ebeddc94375",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading pytorch_model.bin:   0%|          | 0.00/670M [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b04454bb93bf48aaa3ed7ffcb688cb6b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)nce_bert_config.json:   0%|          | 0.00/57.0 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "07a3eda52cfc44e88b2cdd2b960a81a8",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading (…)cial_tokens_map.json:   0%|          | 0.00/125 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0c82adb6d6f14ce8a63b9f7d342f0ea7",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading tokenizer.json:   0%|          | 0.00/712k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f0bb5b2ef2ca43f6b0e0bb8dc715c0eb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading tokenizer_config.json:   0%|          | 0.00/342 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "67b7842417684fd5bb6e609b0df3faa5",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading vocab.txt:   0%|          | 0.00/232k [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ce53f891700e496f954de33851869bcc",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Downloading modules.json:   0%|          | 0.00/385 [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "SentenceTransformer(\n",
       "  (0): Transformer({'max_seq_length': 512, 'do_lower_case': False}) with Transformer model: BertModel \n",
       "  (1): Pooling({'word_embedding_dimension': 1024, 'pooling_mode_cls_token': False, 'pooling_mode_mean_tokens': True, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False})\n",
       "  (2): Normalize()\n",
       ")"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "embedding_model = SentenceTransformer(EMBEDDING_MODEL_NAME)\n",
    "embedding_model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a64dc32-6dbc-4f1e-b950-d4ffa797912c",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Resize Tokenizer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "871a9c3a-c60f-4ff0-9d1a-3025fdad56be",
   "metadata": {},
   "source": [
    "While our tokenizer can represent new subtokens that are part of the vocabulary, it might be very helpful to explicitly add new tokens to our base model (BertModel) in our cast to our transformer. And then we can use [resize_token_embeddings](https://huggingface.co/docs/transformers/main_classes/model#transformers.PreTrainedModel.resize_token_embeddings) to adjust the model's embedding layer prior to fine-tuning. This can be very useful for contextual use cases, especially if many tokens are new or existing tokens have a very different meaning in our context."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fb431961-ee8f-46ca-9325-96eca41d0da0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import re"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "806dbfcf-bb69-4d38-9fc6-afa6c3f62694",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def get_unique_words(texts):\n",
    "    all_text = \" \".join(texts)  # join all texts\n",
    "    all_text = all_text.replace(\"_\", \" \")  # replace underscores (ex. variable names)\n",
    "    words = re.findall(r'\\b[a-zA-Z]+\\b', all_text)  # only letters\n",
    "    words = [word.lower() for word in words]  # lower\n",
    "    return set(words)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f85c5818-3526-4171-b4f8-4e529b81cc9b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 11204/11204 [00:00<00:00, 1936098.47it/s]\n"
     ]
    }
   ],
   "source": [
    "# Get tokens that are OOV (out of vocabulary)\n",
    "new_words = []\n",
    "vocab = embedding_model.tokenizer.get_vocab().keys()\n",
    "texts = [section[\"text\"] for section in sections_ds.take_all()]\n",
    "unique_words = get_unique_words(texts=texts)\n",
    "for word in tqdm(unique_words):\n",
    "    if word not in vocab:\n",
    "        new_words.append(word)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0ee4cfe-7989-4957-9fbe-6fd1360ab504",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5790\n",
      "['estimator', 'txt', 'replicacontext', 'metricsexportport', 'memoryefficientattentionflashattentionop', 'voc', 'disjoint', 'custompredictor', 'ulimit', 'allvalues']\n"
     ]
    }
   ],
   "source": [
    "# Inspect\n",
    "print (len(new_words))\n",
    "print (new_words[:10])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30fc2d0c-1ecd-499f-aa06-918464a45b1e",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "30522\n",
      "36312\n"
     ]
    }
   ],
   "source": [
    "# Add new words to tokenizer\n",
    "print (len(embedding_model.tokenizer))\n",
    "embedding_model.tokenizer.add_tokens(new_words)\n",
    "print (len(embedding_model.tokenizer))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97dd942a-74d2-4650-a87a-f102b75ed206",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Embedding(30522, 1024, padding_idx=0)\n",
      "Embedding(36312, 1024, padding_idx=0)\n"
     ]
    }
   ],
   "source": [
    "# Resize tokenizer\n",
    "print (embedding_model._modules[\"0\"]._modules[\"auto_model\"]._modules[\"embeddings\"]._modules[\"word_embeddings\"])\n",
    "embedding_model._modules[\"0\"]._modules[\"auto_model\"].resize_token_embeddings(len(embedding_model.tokenizer))\n",
    "embedding_model._modules[\"0\"]._modules[\"auto_model\"]._modules[\"embeddings\"]._modules[\"word_embeddings\"].padding_idx = 0\n",
    "print (embedding_model._modules[\"0\"]._modules[\"auto_model\"]._modules[\"embeddings\"]._modules[\"word_embeddings\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf482bee-b507-4166-bf53-60a50984c5d0",
   "metadata": {},
   "source": [
    "## Full parameter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e47066d0-e7e2-47d8-8e55-6f295ece02a0",
   "metadata": {},
   "source": [
    "Our full parameter fine-tuning approach will tune all of the following weights:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aaa5da86-626c-4387-9a83-c73e7c51419d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('auto_model',\n",
       "              BertModel(\n",
       "                (embeddings): BertEmbeddings(\n",
       "                  (word_embeddings): Embedding(36312, 1024, padding_idx=0)\n",
       "                  (position_embeddings): Embedding(512, 1024)\n",
       "                  (token_type_embeddings): Embedding(2, 1024)\n",
       "                  (LayerNorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)\n",
       "                  (dropout): Dropout(p=0.1, inplace=False)\n",
       "                )\n",
       "                (encoder): BertEncoder(\n",
       "                  (layer): ModuleList(\n",
       "                    (0-23): 24 x BertLayer(\n",
       "                      (attention): BertAttention(\n",
       "                        (self): BertSelfAttention(\n",
       "                          (query): Linear(in_features=1024, out_features=1024, bias=True)\n",
       "                          (key): Linear(in_features=1024, out_features=1024, bias=True)\n",
       "                          (value): Linear(in_features=1024, out_features=1024, bias=True)\n",
       "                          (dropout): Dropout(p=0.1, inplace=False)\n",
       "                        )\n",
       "                        (output): BertSelfOutput(\n",
       "                          (dense): Linear(in_features=1024, out_features=1024, bias=True)\n",
       "                          (LayerNorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)\n",
       "                          (dropout): Dropout(p=0.1, inplace=False)\n",
       "                        )\n",
       "                      )\n",
       "                      (intermediate): BertIntermediate(\n",
       "                        (dense): Linear(in_features=1024, out_features=4096, bias=True)\n",
       "                        (intermediate_act_fn): GELUActivation()\n",
       "                      )\n",
       "                      (output): BertOutput(\n",
       "                        (dense): Linear(in_features=4096, out_features=1024, bias=True)\n",
       "                        (LayerNorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)\n",
       "                        (dropout): Dropout(p=0.1, inplace=False)\n",
       "                      )\n",
       "                    )\n",
       "                  )\n",
       "                )\n",
       "                (pooler): BertPooler(\n",
       "                  (dense): Linear(in_features=1024, out_features=1024, bias=True)\n",
       "                  (activation): Tanh()\n",
       "                )\n",
       "              ))])"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "embedding_model._modules[\"0\"]._modules"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea3b63f0-428b-4642-a0db-6e23939db67f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sentence_transformers.losses import MultipleNegativesRankingLoss\n",
    "from torch.utils.data import DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dcdcf10f-71cd-4502-bd6d-d979e01691a9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Training setup\n",
    "num_epochs = 2\n",
    "batch_size = 4\n",
    "train_dataloader = DataLoader(train_dataset, batch_size=batch_size)\n",
    "loss = MultipleNegativesRankingLoss(embedding_model) # MNR Loss\n",
    "warmup_steps = int(0.1 * num_epochs * len(train_dataloader))  # not used"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ecb202a6-d8ac-47d6-a505-7f6ed74d4d44",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0a1c1f00d4424acca9cebe8698797386",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch:   0%|          | 0/2 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "112633295a3b467ba8dff62d0e04a1d9",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Iteration:   0%|          | 0/1146 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "EPOCH: 0, VAL SCORE:0.5425\n",
      "\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "861a6dd2b06641a19d1cecaac246e050",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Iteration:   0%|          | 0/1146 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "EPOCH: 1, VAL SCORE:0.5420\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Train\n",
    "experiment_name = \"gte-large-fine-tuned-fp\"\n",
    "embedding_model_path = str(Path(EFS_DIR, experiment_name))\n",
    "embedding_model.fit(\n",
    "    train_objectives=[(train_dataloader, loss)],\n",
    "    epochs=num_epochs,\n",
    "    warmup_steps=0,\n",
    "    optimizer_params={\"lr\": 1e-8},\n",
    "    weight_decay=0,\n",
    "    output_path=embedding_model_path,\n",
    "    show_progress_bar=True,\n",
    "    evaluator=evaluator,\n",
    "    callback=val_callback)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df298a82-42ba-413c-bc7e-3b8050bf1c3e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Clean up\n",
    "import gc\n",
    "import torch\n",
    "del embedding_model, loss\n",
    "gc.collect()\n",
    "torch.cuda.empty_cache()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "660c77bf-23a6-4690-b7b4-17dda0532cde",
   "metadata": {},
   "source": [
    "Now we're ready to actually apply this fine-tuned embedding model on our test evaluation dataset. We can simply pass in our model artifact directory for the `embedding_model_name` because [HuggingFaceEmbeddings](https://api.python.langchain.com/en/latest/embeddings/langchain.embeddings.huggingface.HuggingFaceEmbeddings.html) accepts a string that can be either a directory or the model's name. If a directory matches with the input string, then it will load the model from that location first before trying to search on HF's hub."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2fecbe01-3b88-4124-9128-1fa0a04ec2cb",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica, you can follow these steps:\n\n1. List the available actors using the command `ray summary actors` or `ray list actors --filter \"class_name=ServeReplica:SleepyPid\"` in the Ray terminal.\n2. Identify the NAME of the replica you want to kill from the list.\n3. Get a handle to the replica using the command `replica_handle = ray.get_actor(\"NAME\", namespace=\"serve\")` in a Python Ray terminal.\n4. Kill the replica using the command `ray.kill(replica_handle, no_restart=True)` in the Python Ray terminal.\n\nFor example, if your replica's NAME is `SERVE_REPLICA::SleepyPid#RlRptP`, you can kill it using the following commands:\n```python\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\nray.kill(replica_handle, no_restart=True)\n```",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer is detailed, accurate, and provides a step-by-step guide on how to kill a specific serve replica. It goes above and beyond the reference answer, which does not provide any information on the query. The generated answer is also technically correct and would be helpful to someone looking for this information.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 5,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-deployments",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#worker-node-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-applications",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#replica-health-checking",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#head-node-failure"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [22:47<00:00,  7.73s/it]\n"
     ]
    }
   ],
   "source": [
    "sql_dump_fp = Path(EFS_DIR, \"sql_dumps\", f\"{experiment_name}_{CHUNK_SIZE}_{CHUNK_OVERLAP}.sql\")\n",
    "run_experiment(\n",
    "    experiment_name=experiment_name, \n",
    "    chunk_size=CHUNK_SIZE, \n",
    "    chunk_overlap=CHUNK_OVERLAP, \n",
    "    num_chunks=NUM_CHUNKS,\n",
    "    embedding_model_name=embedding_model_path,\n",
    "    embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "    llm=llm,  # ensure same model as we did for embedding model experiments\n",
    "    evaluator=EVALUATOR,\n",
    "    docs_dir=DOCS_DIR, \n",
    "    experiments_dir=EXPERIMENTS_DIR, \n",
    "    references_fp=REFERENCES_FILE_PATH,\n",
    "    num_samples=NUM_SAMPLES,\n",
    "    sql_dump_fp=sql_dump_fp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "271f0c48-1962-4ef8-adaf-ce0fe9dc4525",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "embedding_model_names.append(experiment_name)\n",
    "experiment_names = []\n",
    "for embedding_model_name in embedding_model_names:\n",
    "    experiment_names.append(f\"{embedding_model_name.split('/')[-1]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b4a3565-264c-4047-9cee-37dbc27ace8d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gte-base\n",
      "  retrieval score: 0.7570621468926554\n",
      "  quality score: 3.9322033898305087\n",
      "\n",
      "gte-large\n",
      "  retrieval score: 0.7796610169491526\n",
      "  quality score: 3.9350282485875705\n",
      "\n",
      "bge-large-en\n",
      "  retrieval score: 0.4745762711864407\n",
      "  quality score: 3.480225988700565\n",
      "\n",
      "text-embedding-ada-002\n",
      "  retrieval score: 0.6497175141242938\n",
      "  quality score: 3.5395480225988702\n",
      "\n",
      "gte-large-fine-tuned-fp\n",
      "  retrieval score: 0.5141242937853108\n",
      "  quality score: 3.446327683615819\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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DC5dusSFCxeIj4+ncePGRr+Pjw/3798nNzeX0tJS1qxZw+7du2uUj9Vq5ezZs9y9e5dnz57x9etXAJycnIiJiWHOnDn4+PgQGBjoME5YWBgbNmzg4sWL3L17l7y8PObOnUv//v1p2bIlVquVCRMmEBsby549e7hz5w4nTpxg586dACQkJFBWVkZSUhI3btxg7969LFy4kBkzZhjnG6uTmJjIixcvGDNmDOfPn6e0tJRDhw4xceLEagtiERGpXyocRURE6oCPjw/nz5/Hy8uLUaNG4enpSUREBDabjcLCQuMtowArV67Ew8OD4OBgxo4dS0pKit120eHDhzN9+nSmTJlCQEAARUVFLFiwoEb5pKSk4OTkhL+/P25ubnbnI+Pi4qisrGTixIm/jBMeHs6WLVsYMmQIfn5+JCUlER4ebhSGAJmZmURFRZGQkICvry+TJ0/m3bt3AHTs2JG8vDzOnTtH9+7diY+PJy4uzu7lP9Vxd3ensLCQL1++MGTIELp160ZycjKurq4OC04REakfpqqavBtcREREftvChQtZtWoVR44coW/fvg2djuHUqVMMHDiQsrIy2rdv39DpiIjIX0CFo4iISD3Kzs7m9evXTJ06tcFXyioqKnj69CkTJkzAYrGwdevWBs1HRET+HiocRURE/k/k5OQQFxdHQEAA+/bto2PHjg2dkoiI/CVUOIqIiIiIiIhDOl0uIiIiIiIiDqlwFBEREREREYdUOIqIiIiIiIhDKhxFRERERETEIRWOIiIiIiIi4pAKRxEREREREXFIhaOIiIiIiIg4pMJRREREREREHFLhKCIiIiIiIg79C4w1fLVaFwsIAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77dcce5c-953f-4dca-a589-862bb476e98f",
   "metadata": {},
   "source": [
    "This didn't really improve our overall application's retrieval or quality score. This doesn't necessarily mean that fine-tuning is not useful but might not always be worth the effort.\n",
    "- synthetic data is not exactly like the types of questions that users ask (might be worth creating a dataset of more realistic queries or prompt tuning for more synthetic data that is more representative of user queries).\n",
    "- fine-tuning the entire embedding model on our small embedding dataset might be causing **overfitting**.\n",
    "- our experiment's evaluation is on a small dataset so slightly tuning embeddings via MNR may not increase retrieval recall much/if at all."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "baad7054-5af6-4f4b-8257-af9f9172bd26",
   "metadata": {},
   "source": [
    "## Embedding layer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca2b2107-a086-4b48-86a6-94728ddbd822",
   "metadata": {},
   "source": [
    "To help mitigate the overfitting, we can avoid retraining the entire embedding model and freeze all layers except for the embedding layer (word/subtoken embedding only, not the positional or token type layers). **Note**: this approach is somewhat similar to training a separate linear adapter (which we evaluation results for) except that it's larger and requires rebuilding the index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e4ae9f3b-366e-42de-9ad7-32510df31770",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d7fa520-2bf1-4f42-a008-359047913d42",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "BertEmbeddings(\n",
       "  (word_embeddings): Embedding(30522, 1024, padding_idx=0)\n",
       "  (position_embeddings): Embedding(512, 1024)\n",
       "  (token_type_embeddings): Embedding(2, 1024)\n",
       "  (LayerNorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)\n",
       "  (dropout): Dropout(p=0.1, inplace=False)\n",
       ")"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Reinitialize base embedding model\n",
    "embedding_model = SentenceTransformer(EMBEDDING_MODEL_NAME)\n",
    "embedding_model._modules[\"0\"]._modules[\"auto_model\"]._modules[\"embeddings\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6b03f5ed-b938-47d0-95fb-67ee08aaa324",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Unfreeze embedding layers\n",
    "for param in embedding_model._modules[\"0\"]._modules[\"auto_model\"]._modules[\"embeddings\"].parameters():\n",
    "    param.requires_grad = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b6157d3d-103e-4287-bf70-cca745e797bb",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Freeze Bert encoder layers\n",
    "for param in embedding_model._modules[\"0\"]._modules[\"auto_model\"]._modules[\"encoder\"].parameters():\n",
    "    param.requires_grad = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9506f22c-f192-4384-83db-0aa108494f7b",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Training setup\n",
    "num_epochs = 2\n",
    "batch_size = 4\n",
    "train_dataloader = DataLoader(train_dataset, batch_size=batch_size)\n",
    "loss = MultipleNegativesRankingLoss(embedding_model)\n",
    "warmup_steps = int(0.1 * num_epochs * len(train_dataloader))  # not used"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff787441-13fc-4d18-8eca-d76f691f0940",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1ff0d8c3cab1466ea41a0e368aeafbcb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Epoch:   0%|          | 0/2 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "25278952f4414d658e36a4a3774495b4",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Iteration:   0%|          | 0/1146 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "EPOCH: 0, VAL SCORE:0.7930\n",
      "\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ca65307eb6a345079c88ce3bd2c7afdb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Iteration:   0%|          | 0/1146 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "EPOCH: 1, VAL SCORE:0.7963\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Train\n",
    "experiment_name = \"gte-large-fine-tuned-el\"\n",
    "embedding_model_path = str(Path(EFS_DIR, experiment_name))\n",
    "embedding_model.fit(\n",
    "    train_objectives=[(train_dataloader, loss)],\n",
    "    epochs=num_epochs,\n",
    "    warmup_steps=0,\n",
    "    optimizer_params={\"lr\": 1e-5},\n",
    "    weight_decay=0,\n",
    "    output_path=embedding_model_path,\n",
    "    show_progress_bar=True,\n",
    "    evaluator=evaluator,\n",
    "    callback=val_callback)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15778bc3-8ff0-409e-8396-fc2acb6d7e9b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica, you can follow these steps:\n\n1. Get a list of all actors by running `ray summary actors` or `ray list actors` command.\n2. Identify the NAME of the specific replica you want to kill from the list.\n3. Get a handle to the replica by running `ray.get_actor(\"NAME\", namespace=\"serve\")` in a Python interpreter.\n4. Kill the replica by running `ray.kill(replica_handle, no_restart=True)` in the Python interpreter.\n\nFor example, if you want to kill the replica with NAME \"SERVE_REPLICA::SleepyPid#RlRptP\", you can run:\n```python\nimport ray\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\nray.kill(replica_handle, no_restart=True)\n```\nThis will kill the specific replica without restarting it, and you can confirm its death by checking the list of actors again.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer is highly detailed and provides a step-by-step guide on how to kill a specific serve replica. It even includes a practical example. The reference answer does not provide any useful information, making the generated answer superior.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 5,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-deployments",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#worker-node-failure",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-applications",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#replica-health-checking",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#head-node-failure"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [21:29<00:00,  7.28s/it]\n"
     ]
    }
   ],
   "source": [
    "# Experiment\n",
    "sql_dump_fp = Path(EFS_DIR, \"sql_dumps\", f\"{experiment_name}_{CHUNK_SIZE}_{CHUNK_OVERLAP}.sql\")\n",
    "run_experiment(\n",
    "    experiment_name=experiment_name, \n",
    "    chunk_size=CHUNK_SIZE,\n",
    "    chunk_overlap=CHUNK_OVERLAP, \n",
    "    num_chunks=NUM_CHUNKS,\n",
    "    embedding_model_name=embedding_model_path,\n",
    "    embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "    llm=llm,  # ensure same model as we did for embedding model experiments\n",
    "    evaluator=EVALUATOR,\n",
    "    docs_dir=DOCS_DIR, \n",
    "    experiments_dir=EXPERIMENTS_DIR, \n",
    "    references_fp=REFERENCES_FILE_PATH,\n",
    "    num_samples=NUM_SAMPLES,\n",
    "    sql_dump_fp=sql_dump_fp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3fcd9a29-2e15-46ac-9443-3e87632888a6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "embedding_model_names.append(experiment_name)\n",
    "experiment_names = []\n",
    "for embedding_model_name in embedding_model_names:\n",
    "    experiment_names.append(f\"{embedding_model_name.split('/')[-1]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eb87087-437f-4a76-9890-e47f457d81be",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gte-base\n",
      "  retrieval score: 0.7570621468926554\n",
      "  quality score: 3.9322033898305087\n",
      "\n",
      "gte-large\n",
      "  retrieval score: 0.7796610169491526\n",
      "  quality score: 3.9350282485875705\n",
      "\n",
      "bge-large-en\n",
      "  retrieval score: 0.4745762711864407\n",
      "  quality score: 3.480225988700565\n",
      "\n",
      "text-embedding-ada-002\n",
      "  retrieval score: 0.6497175141242938\n",
      "  quality score: 3.5395480225988702\n",
      "\n",
      "gte-large-fine-tuned-fp\n",
      "  retrieval score: 0.5141242937853108\n",
      "  quality score: 3.446327683615819\n",
      "\n",
      "gte-large-fine-tuned-el\n",
      "  retrieval score: 0.7909604519774012\n",
      "  quality score: 3.8728813559322033\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA44AAAEqCAYAAABa5Q5FAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAACZJ0lEQVR4nOzdeVzNafsH8M/paO9U0k51kqKGpKjJPiNqxpLhsUbxS2GEapAtZKkxI1meeWQZhWHqmUEMphmMSFHRhpo2LYbK2kqLzv37o+n7OG2KFsb1fr3Oazr39/7e93VOarrOvfEYYwyEEEIIIYQQQkgTJDo7AEIIIYQQQggh7zZKHAkhhBBCCCGENIsSR0IIIYQQQgghzaLEkRBCCCGEEEJIsyhxJIQQQgghhBDSLEocCSGEEEIIIYQ0ixJHQgghhBBCCCHNosSREEIIIYQQQkizKHEkhBBCCCGEENIsShwJIYQQQkiL8Xg8hIWFdXYYTWKMwdXVFSoqKuDxeEhMTMTIkSPh7u7e2aF1uIiICPB4PBQVFb1VO+/695x0DEocCSGEEEL+4YRCIXbs2NHZYXSI8PBwBAcH48yZM8jPz0ffvn1x4sQJbNq0qd37/lATVPJh6NLZARBCCCGEkA9HVVUVpKSk2q39rKwsaGlpYfDgwVyZiopKu/VHyIeCEscPkEgkwoMHDyAQCMDj8To7HEIIIYS8pdLSUnh4eODs2bMQCARYunQpzp07h379+uHWrVvIzc2Fh4cHPDw8AADFxcUAgGvXrsHHxwcJCQno1q0bxo0bh/Xr10NeXr7Z/p4/f46SkhIAwLp163DmzBk8ePAA6urqmDp1Kry8vCApKQkA8PPzw9mzZ+Hi4oJt27bh3r17KCoqQnp6OhYvXoyEhAQIhUJs3boVEydOxNGjRzFu3DgAwF9//YU1a9bg0qVL4PF4GDx4ML7++mvo6ek1GtfChQtx7NgxALXTK3V1dXHr1i2MHTsW/fr1w9dffw0A6NevH5ycnJCdnY2wsDAoKytj2bJlmDt3LtfWm/R9+fJlXL58GTt37gQAJCcn4+rVq1i1ahXy8vK4umfOnIGDgwP3fah7j9zc3LBlyxYUFRXBxsYGu3btgkAgAFD791tAQACCg4Px8OFD9OrVC8uXL8fEiRO5dn///XesXLkS9+/fx6BBgzBjxgwAQElJCSQkmp5oePbsWXz99ddIS0uDpqYmZs6ciWXLlqFLl/+lCq9+z8k/C2MMpaWl0NbWbvbfCRhpV//+97+Znp4ek5aWZpaWliwmJqbZ+gEBAczIyIjJyMiwHj16MHd3d/bixYu3arO+e/fuMQD0oAc96EEPetCDHvSgBz3owQCwe/fuNZtD0IhjOwoNDYWnpycCAwNhZWWFHTt2wNbWFmlpaVBXV29Q/9ixY1i5ciUOHjyIwYMHIz09HXPmzAGPx8P27dvfqM3G1H1yde/ePSgqKrbdCyaEEEJIhystLYW+vj4OHDjAjT4VFxejT58+cHJywtdff41+/fph4cKF+PLLL7n73NzcwOfzudExoHYE8vPPP0d+fj5kZGQa7U9JSUlsVLC+Xbt24fjx47h8+TKA2tE0f39//Pnnn1BVVQUAXLhwAdOmTUNKSgo0NDQAAJcuXRIbcQwNDcW3336LuLg4boZUVVUVdHV1cfToUYwaNarR/v/zn/9gz549uHXrFlfW2IijtbU19u3bBwBgjMHQ0BCrVq2Cs7PzG/ddvx8AOHr0aItGHHft2oX09HTu7zRvb29ER0fj4sWLqKyshFAoxKlTp2Bpacm14+bmhhcvXuD777+Hj48Pzp07h5iYGO76+vXrsWPHDuTm5kJZWbnRmCdMmIARI0bgq6++4spCQ0Oxbt06pKWlAXj995y830pKSqCjo8P922sKJY7taPv27XBxceGmPQQGBuLs2bM4ePAgVq5c2aB+dHQ0hgwZgpkzZwKoXcg+Y8YMsV8ArW2zMXW/ABUVFSlxJIQQQt5z2dnZqK6uxsiRI7n/rysqKqJ3796QkpKCoqIieDweZGRkxP6/n5qaiuTkZPz0009cGWMMIpEIT548wcmTJ+Hr68tdS0lJga6uLgBATk6Oays0NBS7du1CVlYWysrK8PLlS7G/MaSlpaGnp4eePXtybd27dw86OjowNDTkyj755BOxttPT03H37l10795d7PVWVFSgoKAASUlJ+Oyzz7jyvXv3wsHBATIyMuDxeGKvlc/nc+8FUPu3kIWFhVgdLS0tlJaWvlXf9fsBAFlZWe57UkdOTk6sTFpaGkKhUKw/oVCIX375BYqKirhz5w6eP38uNi0VqE1mBwwYAEVFRdy9exfW1tZi/YwcORI7duzgvh8KCgrctVmzZiEwMBB37txBTEwM/P39uWs1NTWoqKhAly5duFhf/Z6Tf6bXLWGjxLGdVFVV4ebNm1i1ahVXJiEhARsbG1y7dq3RewYPHowffvgBsbGxsLS0xN27d3Hu3DnMnj37jdsEgMrKSlRWVnLPaX46IYQQQsrKyjB//nwsWbKkwTVdXV0sWLAAU6dO5cq0tbUb1Lt27RocHBzg4+MDW1tbKCkpISQkRCwJAfDaNZNNxWdhYYGjR482uKampgYpKSkkJiZyZXUjly1VtwazDo/Hg0gkavO+JSQkwBgTK6uurm51PEDtWsT6yay0tHSTfdf3asx1SWBZWRl8fHwwadKkBvWbGnUmHyZKHNvJ48ePUVNT0+AXiYaGBv78889G75k5cyYeP36MoUOHgjGGly9fYsGCBVi9evUbtwnUTn/w8fF5y1dECCGEkHdRz549ISkpibi4OG5EsLi4GOnp6Rg+fDgAQEpKCjU1NWL3mZubIyUlBb169Wq0XRUVldfuRhodHQ09PT2sWbOGK8vNzX1tzL1798a9e/dQWFjI/V0TFxfXIL7Q0FCoq6s3OdLVVOxv6037bux9VlNTQ2lpKcrLy7kE+tUEriVMTEwgLS2NvLw8jBgxotE6xsbGOH36tFjZ9evXXxuzubk50tLS2u29fJ/weDycPHmywcguqUXnOL5DIiIi4Ovri//85z+Ij4/HiRMncPbs2bc+d2jVqlUoLi7mHvfu3WujiAkhhBDS2QQCAZycnLB8+XJcunQJd+7cgbOzMyQkJLipZ0KhEFeuXMH9+/fx+PFjAICXlxeio6Ph5uaGxMREZGRk4NSpU3Bzc2tx34aGhsjLy0NISAiysrKwa9cunDx58rX3jR49GgYGBnByckJycjKioqKwdu1aAP+bLufg4ABVVVXY29sjMjIS2dnZiIiIwJIlS/DXX3+19m1qlTftWygUIiYmBjk5OXj8+DFEIhGsrKwgJyeH1atXIysrC8eOHUNwcHCr4hEIBFi2bBk8PDxw6NAhZGVlIT4+Hrt378ahQ4cAAAsWLEBGRgaWL1+OtLS0Fvezbt06HD58GD4+Prh1Kxl/nA6Dv886LJnvCpGo5rX3d7YP6YzSzkaJYztRVVUFn89HYWGhWHlhYSE0NTUbvcfb2xuzZ8/GvHnz0K9fP3zxxRfw9fWFn58fRCLRG7UJ1E5hqJvbTusaCSGEkH+e7du3w9raGuPGjYONjQ2GDBkCY2Njbqrhxo0bkZOTAwMDA6ipqQEATE1NcfnyZaSnp2PYsGEYMGAA1q1b1+iU1KZMmDABHh4ecHNzg5mZGaKjo+Ht7f3a+/h8PsLCwlBWVoZBgwZh3rx53KhlXcxycnK4cuUKdHV1MWnSJBgbG8PZ2RkVFRXt/rfMm/a9bNky8Pl8mJiYQE1NDXl5eVBRUcEPP/zAHY/y448/YsOGDa2OadOmTfD29oafnx+MjY1hZ2eHs2fPQl9fH0Dt9OLjx48jLCwM/fv3R2BgoNga1abY2trizJkzOH38OCzMzTH2X1Owc9du5N+8jv2LnJERE93qWD9UVVVVnR1C+2rVOQ6kVSwtLZmbmxv3vKamhnXv3p35+fk1Wt/c3JytWLFCrOzYsWNMVlaWvXz58o3abExxcTEDwIqLi1vzcgghhBDynigrK2NKSkrswIEDnR1Ki129epUBYJmZmZ0dygcn/XoU2zZ1bJOP9OtRnRZbSUkJmzlzJpOTk2Oampps+/btbMSIEWzp0qVsxIgRDY6UqBMZGcmGDh3KHXG3ePFiVlZW1mxfANjJkye55ytWrGCGhoZMVlaW6evrs7Vr17Kqqiru+vr161n//v3Z/v37mVAoZDwejzHGWGpqKhsyZAiTlpZmxsbG7Pz58w3azsvLY1OmTGFKSkqsa9eubMKECSw7O7tN3rPWamluQCOO7cjT0xP79+/HoUOHkJqaioULF6K8vJzbEdXR0VFso5vx48djz549CAkJQXZ2Ns6fPw9vb2+MHz8efD6/RW0SQggh5MOTkJCAH3/8kZvC6ODgAACwt7fv5MiadvLkSZw/fx45OTm4cOECXF1dMWTIEBgYGHR2aB8UkagGfwTva7bOpUP7Om3aqqenJ6KionD69GmcP38ekZGRiI+PBwCcOHECPXr0wMaNG5Gfn4/8/HwAQFZWFuzs7DB58mQkJycjNDQUV69ebdU0bKB2inBwcDBSUlKwc+dO7N+/HwEBAWJ1MjMzcfz4cZw4cQKJiYmoqanBxIkTIScnh5iYGOzbt09sDTBQuzmSra0tBAIBIiMjERUVBQUFBdjZ2b3To5a0OU47mjZtGh49eoR169ahoKAAZmZmCA8P5xaB5+XlQULif7n72rVrwePxsHbtWty/fx9qamoYP348tmzZ0uI2CSGEEPJh2rZtG9LS0iAlJQULCwtERkZy5ya+i0pLS+Hl5YW8vDyoqqrCxsamwW6spP3dT72DsqePm61T+uQx7qfegc5Hph0U1d/9lpbi0KFDOHbsGHd2ZlBQEDedWkVFBXw+HwKBQGzZlp+fHxwcHODu7g6gdi3url27MGLECOzZs6fFu8XWrbsFatdSLlu2DCEhIVixYgVXXlVVhcOHD3NTwMPDw5GVlYWIiAgupi1btmD06NHcPaGhoRCJRDhw4AC3pjcoKAjKysqIiIjAmDFjWvtWdQhKHNuZm5tbk59uREREiD3v0qUL1q9fj/Xr179xm4QQQgj58AwYMAA3b97s7DBaxdHREY6Ojp0dxgevrOhZm9ZrS3fv3kV1dTUsLS25MiUlJfTu3bvZ+5KSkpCcnCx2nAr7+4zS7OzsZs8ofVVTZ5S+Sk9Pj0saASAtLQ06Ojpiieyr8dfFl5mZCYFAIFZeUVGBrKysZl9bZ6LEkRBCCCGEkA+UgnLXNq33Lnjfzyh9V1HiSAghhBBCyAequ/FHUFBRbXa6qqCbKrobf9SBUdX6kM8ofRfR5jiEEEIIIYR8oCQk+Ph0jmuzdT5xcoWEBL+DIvqff+QZpaIaOAwRQlVRBva2IxF5OaJDzyh9G5Q4EkIIIYQQ8gEztBqMCZ6roaAivpmSoJsqJniuhqHV4E6K7B92RmlhArCjL+T++y9c+VcpdF/cxqSxNjDu07vDzih9GzzGGOvsIEjHKikpgZKSEoqLi9/pf5yEEEIIIaTjiEQ1tbusFj2DgnJXdDf+qFNGGptTXl6O7t27w9/fH87Ozp0dTotERUVh6NChyFysAAOV+uN2tSOnmHoYMJnQ4bEBLc8NaI0jIYQQQgghBBIS/A4/cuN1EhIS8Oeff8LS0hLFxcXYuHEjgHf/jFIFBQUYGhoiMzMTS5cuxRChbCNJIwAwADwgfCXQZyzwjiXqr6LEkRBCCCGEEPLOeu/PKP24H/x75jVzBwNK7gO50YD+sA6Ls7VoquoHiKaqEkIIIYQQ0kFu/Qwcb8G02snfA/3+1f7x1NPS3IA2xyGEEEIIIYR8sHg8HsLCwtqvAwWNtq3XSShxJIQQQgghhLxXhEIhduzY0dlhtIzeYEBRG9xGOA3wAMXutfXeYZQ4EkIIIYQQQkh7keADdlv/flI/efz7ud3X7/TGOAAljoQQQgghhJB3TGlpKRwcHCAvLw8tLS0EBARg5MiRcHd3x8iRI5GbmwsPDw/weDzweP9Lxq5evYphw4ZBVlYWOjo6WLJkCcrLy1/bX35+Pj777DPIysqiZ8+e+Pnnn8Wue3l5wcjICHJycujZsye8vb1RXV3NXU9KSsInn3wCgUAARUVFWFhY4MaNG/+L66kKhv3SHbJbSqATUIolv1agvIrVjkR24lEcrUGJIyGEEEIIIeSd4unpiaioKJw+fRrnz59HZGQk4uPjAQAnTpxAjx49sHHjRuTn5yM/Px8AkJWVBTs7O0yePBnJyckIDQ3F1atX4ebm9tr+vL29MXnyZCQlJcHBwQHTp09Hamoqd10gECA4OBgpKSnYuXMn9u/fj4CAAO66g4MDevTogbi4ONy8eRMrV66EpKSkeFyzXZB8OwWh3/8bV0u6wy19KOB+671IGgHaVfWDRLuqEkIIIYSQd1VpaSm6deuGY8eO4V//qt1ltLi4GNra2nBxccGOHTsgFArh7u4Od3d37r558+aBz+dj7969XNnVq1cxYsQIlJeXQ0ZGptH+eDweFixYgD179nBlH3/8MczNzfGf//yn0Xu2bduGkJAQblRRUVERu3fvhpOTU4O6bxpXR2lpbkDnOBJCCCGEEELeGXfv3kV1dTUsLS25MiUlJfTu3bvZ+5KSkpCcnIyjR49yZYwxiEQiZGdn4+TJk/D19eWupaSkQFdXFwBgbW0t1pa1tTUSExO556Ghodi1axeysrJQVlaGly9fiiVZnp6emDdvHo4cOQIbGxtMmTIFBgYGLYrL2Ni4Fe9O56GpqoQQQgghhJD3XllZGebPn4/ExETukZSUhIyMDBgYGGDBggVi17S1tVvU7rVr1+Dg4IDPP/8cZ86cQUJCAtasWYOqqiquzoYNG3Dnzh2MHTsWf/zxB0xMTHDy5MkWxfW+oBFHQgghhBBCyDujZ8+ekJSURFxcHDciWFxcjPT0dAwfPhwAICUlhZqaGrH7zM3NkZKSgl69ejXaroqKClRUVBq9dv36dTg6Ooo9HzBgAAAgOjoaenp6WLNmDXc9Nze3QRtGRkYwMjKCh4cHZsyYgaCgIHzxxRevjet9QSOOhBBCCCGEkHeGQCCAk5MTli9fjkuXLuHOnTtwdnaGhIQEt4OqUCjElStXcP/+fTx+/BhA7c6n0dHRcHNzQ2JiIjIyMnDq1KkWbY7z008/4eDBg0hPT8f69esRGxvL3WdoaIi8vDyEhIQgKysLu3bt4kYTAeDFixdwc3NDREQEcnNzERUVhbi4OBgbG6NGxGA3cz4io6Lwr9nzcDM+oVVxvUsocSSEEEIIIYS8U7Zv3w5ra2uMGzcONjY2GDJkCIyNjbmNZDZu3IicnBwYGBhATU0NAGBqaorLly8jPT0dw4YNw4ABA7Bu3boWTUn18fFBSEgITE1NcfjwYfz4448wMTEBAEyYMAEeHh5wc3ODmZkZoqOj4e3tzd3L5/Px5MkTODo6wsjICFOnTsVnn32GIVMXYOjWP+AdWQqVqb44ezUeltZDYNrfrMVxvUtoV9UPEO2qSgghhBBC3ifl5eXo3r07/P394ezs3NnhvFb47Xws/CEe9ROtuhMn98wyh11frY4Oq1EtzQ1oxJEQQgghhBDyTklISMCPP/6IrKwsxMfHw8HBAQBgb2/fyZG9Xo2IweeXlAZJIwCuzOeXFNSI3q/xO0ocCSGEEEIIIe+cbdu2oX///rCxsUF5eTkiIyOhqqra2WG9Vmz2U+QXVzR5nQHIL65AbPbTjguqDVDi2M6+++47CIVCyMjIwMrKCrGxsU3WHTlyJHg8XoPH2LFjuTpz5sxpcN3Ozq4jXgohhBBCCCEdYsCAAbh58ybKysrw9OlTnD9/Hv369evssFrkYWnTSeOb1HtX0HEc7Sg0NBSenp4IDAyElZUVduzYAVtbW6SlpUFdXb1B/RMnToidB/PkyRP0798fU6ZMEatnZ2eHoKAg7rm0tHT7vQhCCCGEEEJIi6kLZNq03ruCRhzb0fbt2+Hi4oK5c+fCxMQEgYGBkJOTw8GDBxutr6KiAk1NTe5x/vx5yMnJNUgcpaWlxep17dq1I14OIYQQQggh5DUs9VWgpSTDbYRTHw+AlpIMLPUbP1PyXUWJYzupqqrCzZs3YWNjw5VJSEjAxsYG165da1Eb33//PaZPnw55eXmx8oiICKirq6N3795YuHAhnjx50mw7lZWVKCkpEXsQQgghhBBC2h5fgof142uP8qifPNY9Xz/eBHyJplLLdxMlju3k8ePHqKmpgYaGhli5hoYGCgoKXnt/bGwsbt++jXnz5omV29nZ4fDhw7h48SK2bt2Ky5cv47PPPkNNTU2Tbfn5+UFJSYl76OjovNmLIoQQQgghhLyWXV8t7JllDk0l8emomkoy79RRHK1BaxzfUd9//z369esHS0tLsfLp06dzX/fr1w+mpqYwMDBAREQERo0a1Whbq1atgqenJ/e8pKSEkkdCCCGEEELakV1fLYw20URs9lM8LK2AuqB2eur7NtJYhxLHdqKqqgo+n4/CwkKx8sLCQmhqajZ7b3l5OUJCQrBx48bX9tOzZ0+oqqoiMzOzycRRWlqaNtAhhBBCCCGkg/EleLA26NbZYbQJmqraTqSkpGBhYYGLFy9yZSKRCBcvXoS1tXWz9/7000+orKzErFmzXtvPX3/9hSdPnkBL6/0b7iaEEEIIIYS8HyhxbEeenp7Yv38/Dh06hNTUVCxcuBDl5eWYO3cuAMDR0RGrVq1qcN/333+PiRMnols38U8nysrKsHz5cly/fh05OTm4ePEi7O3t0atXL9ja2nbIayKEEEIIIYR8eGiqajuaNm0aHj16hHXr1qGgoABmZmYIDw/nNszJy8uDhIR47p6WloarV6/i999/b9Aen89HcnIyDh06hKKiImhra2PMmDHYtGkTTUUlhBBCCCGEtBseY4x1dhCkY5WUlEBJSQnFxcVQVFTs7HAIIYQQQgghnaSluQFNVSWEEEIIIYQQ0ixKHAkhhBBCCCGENIsSR0IIIYQQQgghzaLEkRBCCCGEEEJIsyhxJIQQQgghhBDSLEocCSGEEEIIIYQ0ixJHQgghhBBCCCHNosSREEIIIYQQQkizKHEkhBBCCCGEENIsShwJIYQQQgghhDSLEkdCCCGEEEIIIc2ixJEQQgghhBBCSLMocSSEEEIIIYQQ0ixKHAkhhBBCCCGENIsSR0IIIYQQQgghzaLEkRBCCCGEEEJIsyhxJIQQQgghhBDSLEocCSGEEEIIIYQ0ixJHQgghhBBCCCHNosSREEIIIYQQQkizKHFsxMuXL3HhwgXs3bsXpaWlAIAHDx6grKyskyMjhBBCCCGEkI7XpbMDeNfk5ubCzs4OeXl5qKysxOjRoyEQCLB161ZUVlYiMDCws0MkhBBCCCGEkA5FI471LF26FAMHDsSzZ88gKyvLlX/xxRe4ePFiq9v77rvvIBQKISMjAysrK8TGxjZZd+TIkeDxeA0eY8eO5eowxrBu3TpoaWlBVlYWNjY2yMjIaHVchBBCCCGEENJSlDjWExkZibVr10JKSkqsXCgU4v79+61qKzQ0FJ6enli/fj3i4+PRv39/2Nra4uHDh43WP3HiBPLz87nH7du3wefzMWXKFK7ON998g127diEwMBAxMTGQl5eHra0tKioqWv9iCSGEEEIIIaQFKHGsRyQSoaampkH5X3/9BYFA0Kq2tm/fDhcXF8ydOxcmJiYIDAyEnJwcDh482Gh9FRUVaGpqco/z589DTk6OSxwZY9ixYwfWrl0Le3t7mJqa4vDhw3jw4AHCwsJa/VoJIYQQQgghpCUocaxnzJgx2LFjB/ecx+OhrKwM69evx+eff97idqqqqnDz5k3Y2NhwZRISErCxscG1a9da1Mb333+P6dOnQ15eHgCQnZ2NgoICsTaVlJRgZWXV4jYJIYQQQgghpLVoc5x6tm3bBjs7O5iYmKCiogIzZ85ERkYGVFVV8eOPP7a4ncePH6OmpgYaGhpi5RoaGvjzzz9fe39sbCxu376N77//nisrKCjg2qjfZt21xlRWVqKyspJ7XlJS0qLXQAghhBBCCCEAJY4N6OjoICkpCaGhoUhKSkJZWRmcnZ3h4OAgtllOe/v+++/Rr18/WFpavnVbfn5+8PHxaYOoCCGEEEIIIR8iShxfUV1djT59+uDMmTNwcHCAg4PDG7elqqoKPp+PwsJCsfLCwkJoamo2e295eTlCQkKwceNGsfK6+woLC6GlpSXWppmZWZPtrVq1Cp6entzzkpIS6OjotPSlEEIIIYS0KZFIhKqqqs4Og5APgqSkJPh8/lu3Q4njKyQlJdtsd1IpKSlYWFjg4sWLmDhxIoDaX5IXL16Em5tbs/f+9NNPqKysxKxZs8TK9fX1oampiYsXL3KJYklJCWJiYrBw4cIm25OWloa0tPRbvR5CCCGEkLZQVVWF7OxsiESizg6FkA+GsrIyNDU1wePx3rgNShzrWbRoEbZu3YoDBw6gS5e3e3s8PT3h5OSEgQMHwtLSEjt27EB5eTnmzp0LAHB0dET37t3h5+cndt/333+PiRMnolu3bmLlPB4P7u7u2Lx5MwwNDaGvrw9vb29oa2tzySkhhBBCyLuKMYb8/Hzw+Xzo6OhAQoL2aSSkPTHG8Pz5c+44wFdnLbYWJY71xMXF4eLFi/j999/Rr18/bkfTOidOnGhxW9OmTcOjR4+wbt06FBQUwMzMDOHh4dzmNnl5eQ1+YaalpeHq1av4/fffG21zxYoVKC8vh6urK4qKijB06FCEh4dDRkamla+UEEIIIaRjvXz5Es+fP4e2tjbk5OQ6OxxCPgh1+7Q8fPgQ6urqbzxtlccYY20Z2PuubjSwKUFBQR0USfspKSmBkpISiouLoaio2NnhEEIIIeQDUVFRgezsbAiFwg7ddJCQD92LFy+Qk5MDfX39BgNOLc0NaMSxnn9CYkgIIYQQ8i57m3VWhJDWa4ufOUocm/Do0SOkpaUBAHr37g01NbVOjogQQgghhBBCOgetSK6nvLwc//d//wctLS0MHz4cw4cPh7a2NpydnfH8+fPODo8QQgghhJAOM2fOHNqEkQCgxLEBT09PXL58Gb/88guKiopQVFSEU6dO4fLly/jqq686OzxCCCGEEPIPM2fOHPB4vAYPOzu7zg4NO3fuRHBwcGeHAaB2umVYWFhnh/HBoqmq9Rw/fhw///wzRo4cyZV9/vnnkJWVxdSpU7Fnz57OC44QQgghhPwj2dnZNdhrozPP4a6pqQGPx4OSklKnxUDeLTTiWM/z58+54zJepa6uTlNVCSGEEEJIu5CWloampqbYo2vXroiIiICUlBQiIyO5ut988w3U1dVRWFgIABg5ciTc3Nzg5uYGJSUlqKqqwtvbG68enlBZWYlly5ahe/fukJeXh5WVFSIiIrjrwcHBUFZWxunTp2FiYgJpaWnk5eU1mKo6cuRILF68GO7u7ujatSs0NDSwf/9+7qxygUCAXr164ddffxV7fbdv38Znn30GBQUFaGhoYPbs2Xj8+LFYu0uWLMGKFSugoqICTU1NbNiwgbsuFAoBAF988QV4PB73PCkpCZ988gkEAgEUFRVhYWGBGzduvOV3gzSGEsd6rK2tsX79elRUVHBlL168gI+PD6ytrTsxMkIIIYQQ8qEZOXIk3N3dMXv2bBQXFyMhIQHe3t44cOCA2GDHoUOH0KVLF8TGxmLnzp3Yvn07Dhw4wF13c3PDtWvXEBISguTkZEyZMgV2dnbIyMjg6jx//hxbt27FgQMHcOfOHairqzca06FDh6CqqorY2FgsXrwYCxcuxJQpUzB48GDEx8djzJgxmD17NjfoUlRUhE8//RQDBgzAjRs3EB4ejsLCQkydOrVBu/Ly8oiJicE333yDjRs34vz58wBqz1oHak9AyM/P5547ODigR48eiIuLw82bN7Fy5UpISkq2wTtPGmBEzK1bt5i2tjbr1q0b+/TTT9mnn37KunXrxrp3785u377d2eG1ieLiYgaAFRcXd3YohBBCCPmAvHjxgqWkpLAXL150dijvFCcnJ8bn85m8vLzYY8uWLYwxxiorK5mZmRmbOnUqMzExYS4uLmL3jxgxghkbGzORSMSVeXl5MWNjY8YYY7m5uYzP57P79++L3Tdq1Ci2atUqxhhjQUFBDABLTExsEJu9vb1YX0OHDuWev3z5ksnLy7PZs2dzZfn5+QwAu3btGmOMsU2bNrExY8aItXvv3j0GgKWlpTXaLmOMDRo0iHl5eXHPAbCTJ0+K1REIBCw4OJiR5jX3s9fS3IDWONbTt29fZGRk4OjRo/jzzz8BADNmzICDgwMdVEsIIYQQQtrFJ5980mAvDRUVFQCAlJQUjh49ClNTU+jp6SEgIKDB/R9//LHYWX3W1tbw9/dHTU0Nbt26hZqaGhgZGYndU1lZiW7dunHPpaSkYGpq+tpYX63D5/PRrVs39OvXjyurGwl9+PAhgNrppJcuXYKCgkKDtrKysri46vetpaXFtdEUT09PzJs3D0eOHIGNjQ2mTJkCAwOD174G0nqUODZCTk4OLi4unR0GIYQQQgj5QMjLy6NXr15NXo+OjgYAPH36FE+fPoW8vHyL2y4rKwOfz8fNmzfB5/PFrr2azMnKyrbooPj6U0F5PJ5YWV0bIpGI63/8+PHYunVrg7a0tLSabbeujaZs2LABM2fOxNmzZ/Hrr79i/fr1CAkJwRdffPHa10FahxLHevz8/KChoYH/+7//Eys/ePAgHj16BC8vr06KjBBCCCGEfIiysrLg4eGB/fv3IzQ0FE5OTrhw4QIkJP63XUlMTIzYPdevX4ehoSH4fD4GDBiAmpoaPHz4EMOGDevo8GFubo7jx49DKBSiS5c3Tz8kJSVRU1PToNzIyAhGRkbw8PDAjBkzEBQURIljO6DNcerZu3cv+vTp06D8o48+QmBgYCdERAghhBBC/ukqKytRUFAg9nj8+DFqamowa9Ys2NraYu7cuQgKCkJycjL8/f3F7s/Ly4OnpyfS0tLw448/Yvfu3Vi6dCmA2sTKwcEBjo6OOHHiBLKzsxEbGws/Pz+cPXu23V/bokWL8PTpU8yYMQNxcXHIysrCb7/9hrlz5zaaCDZFKBTi4sWLKCgowLNnz/DixQu4ubkhIiICubm5iIqKQlxcHIyNjdvx1Xy4aMSxnoKCArEh8zpqamrIz8/vhIgIIYQQQsg/XXh4eIO/QXv37o2ZM2ciNzcXZ86cAVA7tXPfvn2YMWMGxowZg/79+wMAHB0d8eLFC1haWoLP52Pp0qVwdXXl2goKCsLmzZvx1Vdf4f79+1BVVcXHH3+McePGtftr09bWRlRUFLy8vDBmzBhUVlZCT08PdnZ2YqOmr+Pv7w9PT0/s378f3bt3R3p6Op48eQJHR0cUFhZCVVUVkyZNgo+PTzu+mg8Xj7FXDnghMDQ0xPr16zFr1iyx8iNHjmD9+vW4e/duJ0XWdkpKSqCkpITi4mIoKip2djjtKjg4GO7u7igqKmrztnk8Hk6ePCl2ttGrcnJyoK+vj4SEBJiZmSEiIgKffPIJnj17BmVl5TaP52296/ERQgh5/1VUVCA7Oxv6+vqQkZHp7HD+MUaOHAkzMzPs2LGjs0Mh76jmfvZamhvQVNV6XFxc4O7ujqCgIOTm5iI3NxcHDx6Eh4fHP37DnLpzgt71Nt9XgwcPRn5+PpSUlDo7lHbFGMO6deugpaUFWVlZ2NjYiJ0RBdQu7HdwcICioiKUlZXh7OyMsrIy7npERATs7e2hpaUFeXl5mJmZ4ejRox39UgghhBBCyN9oqmo9y5cvx5MnT/Dll1+iqqoKACAjIwMvLy+sWrWqk6Mj7zMpKSloamp2dhjt7ptvvsGuXbtw6NAh6Ovrw9vbG7a2tkhJSeE+4XJwcEB+fj7Onz+P6upqzJ07F66urjh27BiA2p3jTE1N4eXlBQ0NDZw5cwaOjo5QUlLqkCk1hBBCCCGknnY5YfIfoLS0lMXGxrJbt26xioqKzg6nTTV2yKeTkxMDIPbIzs5mt27dYnZ2dkxeXp6pq6uzWbNmsUePHjHGGLt06RKTlJRkV65c4drZunUrU1NTYwUFBU222ZTIyEg2dOhQJiMjw3r06MEWL17MysrKuOt6enps06ZNbPbs2UxeXp7p6uqyU6dOsYcPH7IJEyYweXl51q9fPxYXF8fdExQUxJSUlNjJkydZr169mLS0NBszZgzLy8sT6zssLIwNGDCASUtLM319fbZhwwZWXV3NXU9PT2fDhg1j0tLSzNjYmP3+++8NDqGNiYlhZmZmTFpamllYWLATJ04wACwhIYF7vwCwZ8+eicUWHh7O+vTpw+Tl5ZmtrS178OAB12Z1dTVbvHgxU1JSYioqKmzFihXM0dFR7CDexhw+fJhZWFgwBQUFpqGhwWbMmMEKCwvF6pw9e5YZGhoyGRkZNnLkSO7g37r4Hj9+zKZPn860tbWZrKws69u3Lzt27Fiz/YpEIqapqcm+/fZbrqyoqIhJS0uzH3/8kTHGWEpKCgMg9n369ddfGY/Ha3Aw8as+//xzNnfu3Gb7J4QQ8m5r7hByQkj7ae5nr7HcoDE0VbUJCgoKGDRoEAQCAbKysl57hsz7bufOnbC2toaLiwvy8/ORn58PgUCATz/9FAMGDMCNGzcQHh6OwsJCTJ06FcD/pqHOnj0bxcXFSEhIgLe3Nw4cOAANDY1G29TR0Wm0/6ysLNjZ2WHy5MlITk5GaGgorl69Cjc3N7F6AQEBGDJkCBISEjB27FjMnj0bjo6OmDVrFuLj42FgYABHR0ewV5buPn/+HFu2bMHhw4cRFRWFoqIiTJ8+nbseGRkJR0dHLF26FCkpKdi7dy+Cg4OxZcsWALVnEE2aNAlSUlKIiYlBYGBgg2NZysrKMG7cOJiYmODmzZvYsGEDli1b9tr3/fnz59i2bRuOHDmCK1euIC8vT+y+rVu34ujRowgKCkJUVBRKSkoQFhb22narq6uxadMmJCUlISwsDDk5OZgzZw53/d69e5g0aRLGjx+PxMREzJs3DytXrhRro6KiAhYWFjh79ixu374NV1dXzJ49G7GxsU32m52djYKCAtjY2HBlSkpKsLKywrVr1wAA165dg7KyMgYOHMjVsbGxgYSERIOtxF9VXFzMHYRMCCGEEEI6WDslte+d77//nvn7+4uVubi4MAkJCSYhIcGMjY0bjFK9r5r6VGHEiBFs6dKl3PNNmzaxMWPGiNW5d+8eA8DS0tIYY4xVVlYyMzMzNnXqVGZiYsJcXFyabbMpzs7OzNXVVawsMjKSSUhIcJ+M6OnpsVmzZnHX8/PzGQDm7e3NlV27do0BYPn5+Ywxxo2iXb9+nauTmprKALCYmBjGGGOjRo1ivr6+Yn0fOXKEaWlpMcYY++2331iXLl3ERsN+/fVXsRHHvXv3sm7duol9irNnz57XjjgCYJmZmdw93333HdPQ0OCea2hoiI3evXz5kunq6r52xLG+uLg4BoCVlpYyxhhbtWoVMzExEavj5eUlFl9jxo4dy7766qsmr0dFRTEAYqOmjDE2ZcoUNnXqVMYYY1u2bGFGRkYN7lVTU2P/+c9/Gm03NDSUSUlJsdu3bzfZNyGEkHcfjTgS0jloxLEN7du3D127duWeh4eHIygoCIcPH0ZcXByUlZU/uK19k5KScOnSJSgoKHCPujMus7KyANSu2zt69CiOHz+OiooKBAQEvLbdjz76iGvvs88+4/oKDg4W68vW1hYikQjZ2dncvaamptzXGhoaAIB+/fo1KHv48CFX1qVLFwwaNIh73qdPHygrKyM1NZXre+PGjWJ9142SPn/+HKmpqdDR0YG2tjbXhrW1tdhrSk1NhampqdguVfXrNEZOTg4GBgbccy0tLS724uJiFBYWwtLSkrvO5/NhYWHBPT969KhY3JGRkQCAmzdvYvz48dDV1YVAIMCIESMA1J7xVBevlZWVWCz1462pqcGmTZvQr18/qKioQEFBAb/99hvXRlN9t7VLly5h7ty52L9/Pz766KN26YMQQgghhDSPNsf5W0ZGhtjUuVOnTsHe3h4ODg4AAF9fX8ydO7ezwusUZWVlGD9+PLZu3drg2qvnDEVHRwOo3Snz6dOnkJeXb7bdc+fOobq6GgAgKyvL9TV//nwsWbKkQX1dXV3ua0lJSe5rHo/XZFlrphaXlZXBx8cHkyZNanCtvbcKfzV2oDZ+1ooTciZMmCCWAHbv3h3l5eWwtbWFra0tjh49CjU1NeTl5cHW1pbb8Kklvv32W+zcuRM7duxAv379IC8vD3d3d66NxvquO+u0sLBQ7N9IYWEhzMzMAACamppiiT0AvHz5Ek+fPm2wedDly5cxfvx4BAQEwNHRscWxE0IIIYSQtkWJ499evHghdm5JdHQ0nJ2duec9e/ZEQUFBZ4TWYaSkpFBTU8M9Nzc3x/HjxyEUCtGlS+P/VLKysuDh4YH9+/cjNDQUTk5OuHDhAneYa/02AUBPT69BO+bm5khJSUGvXr3a8BXVevnyJW7cuMGN3KWlpaGoqAjGxsZc32lpaU32bWxsjHv37iE/P59Lhq5fv96gzpEjR1BRUcElm/XrtJaSkhI0NDQQFxeH4cOHA6gdBYyPj+eSMIFAAIFAIHbfzZs38eTJE3z99dfcmtIbN240iPf06dNiZfXjjYqKgr29PXemqUgkQnp6OkxMTJrsW19fH5qamrh48SIXY0lJCWJiYrBw4UIAtSObRUVFuHnzJjd6+scff0AkEoklohERERg3bhy2bt0qdoAxIYQQQgjpeDRV9W96enq4efMmAODx48e4c+cOhgwZwl0vKCh4o/P3vvvuOwiFQsjIyMDKyqrZjUUAoKioCIsWLYKWlhakpaVhZGSEc+fOcdc3bNgAHo8n9qibPvq2hEIhYmJikJOTg8ePH2PRokV4+vQpZsyYgbi4OGRlZeG3337D3LlzUVNTg5qaGsyaNQu2traYO3cugoKCkJycDH9//ybbbGok0MvLC9HR0XBzc0NiYiIyMjJw6tSpBpvjvAlJSUksXrwYMTExuHnzJubMmYOPP/6YSyTXrVuHw4cPw8fHB3fu3EFqaipCQkKwdu1aALUbtxgZGcHJyQlJSUmIjIzEmjVrxPqYOXMmeDweXFxckJKSgnPnzmHbtm1vHfvixYvh5+eHU6dOIS0tDUuXLsWzZ8+4kdXG6OrqQkpKCrt378bdu3dx+vRpbNq0SazOggULkJGRgeXLlyMtLQ3Hjh1DcHCwWB1DQ0OcP38e0dHRSE1Nxfz581FYWNhsvDweD+7u7ti8eTNOnz6NW7duwdHREdra2pg4cSKA2qTVzs4OLi4uiI2NRVRUFNzc3DB9+nRoa2uD1dTg3H/+g7F2dlj4r39h0sSJKCgoQEFBAZ4+ffpG7yMhhBBCCHlL7bT+8r3j5+fHNDU12caNG9nIkSPZRx99JHY9ICCAjRo1qlVthoSEMCkpKXbw4EF2584d5uLiwpSVlRsci1CnsrKSDRw4kH3++efs6tWrLDs7m0VERLDExESuzvr169lHH33E8vPzuUfd8Rgt1dQC2LS0NPbxxx8zWVlZ7uiM9PR09sUXXzBlZWUmKyvL+vTpw9zd3ZlIJGI+Pj5MS0uLPX78mGvj+PHjTEpKiou5sTabEhsby0aPHs0UFBSYvLw8MzU1ZVu2bOGu6+npsYCAALF7UO9IjOzsbLENaeqOvDh+/Djr2bMnk5aWZjY2Niw3N1esnfDwcDZ48GAmKyvLFBUVmaWlJdu3b5/YezN06FAmJSXFjIyMWHh4eIO+r127xvr378+kpKSYmZkZO378eIuO43jVyZMn2as/ltXV1czNzY0pKiqyrl27Mi8vLzZlyhQ2ffr0Jt9Hxhg7duwYEwqFTFpamllbW7PTp0+LxcIYY7/88gt3RMmwYcPYwYMHxeJ78uQJs7e3ZwoKCkxdXZ2tXbu2RUeBiEQi5u3tzTQ0NJi0tDQbNWoUt5lSnSdPnrAZM2YwBQUFpqioyObOnctKS0tZ8W+/sfQRI9lERcUGR7kAYCNGjGi2b0IIIe822hynZer/jUHI22qLzXF4jLViQdU/mEgkwoYNG/DLL79AU1MT27dv56YyAsCUKVNgZ2cnNn31daysrDBo0CD8+9//5vrQ0dHB4sWLGxx9AACBgYH49ttv8eeffzZY+1Znw4YNCAsLQ2JiYute4CtKSkqgpKSE4uJisem55N0nEolgbGyMqVOnNhhFfN+V/P477i91B+r/Svp7dLX7zh1QHDOm4wMjhBDSZioqKpCdnQ19ff1230egowmFQri7u8Pd3f2t2+LxeDh58iQ3W4eQt9Xcz15LcwOaqvo3CQkJbNy4EQkJCfj111/FkkYA+Omnn1qVNFZVVeHmzZti59lJSEjAxsaGO8+uvtOnT8Pa2hqLFi2ChoYG+vbtC19f3wZrBDMyMqCtrY2ePXvCwcGB2+WS/PPk5uZi//79SE9Px61bt7Bw4UJkZ2dj5syZnR1am2I1NSj09WuYNAJcWaGvH1i9nwVCCCEfphoRw7WsJziVeB/Xsp6gRkTjIIS0N0oc28njx49RU1PDHQ9RR0NDo8lNdu7evYuff/4ZNTU1OHfuHLy9veHv74/NmzdzdaysrBAcHIzw8HDs2bMH2dnZGDZsGEpLS5uMpbKyEiUlJWIP8n6QkJBAcHAwBg0ahCFDhuDWrVu4cOFCgw823nfPb9zEy+Y2n2IMLwsK8PzGzY4LihBCyDsp/HY+hm79AzP2X8fSkETM2H8dQ7f+gfDb+e3ab2lpKRwcHCAvLw8tLS0EBARg5MiRcHd3x8iRI5GbmwsPDw9uD4o6V69exbBhwyArKwsdHR0sWbIE5eXlr+0vPz8fn332GWRlZdGzZ0/8/PPPYte9vLxgZGQEOTk59OzZE97e3tyu9UDtcWOffPIJBAIBFBUVYWFhIbZZ3pvGRT5clDi+Q0QiEdTV1bFv3z5YWFhg2rRpWLNmDQIDA7k6n332GaZMmQJTU1PY2tri3LlzKCoqwn//+98m2/Xz84OSkhL3qNtpk7z7dHR0EBUVheLiYpSUlCA6OprbYfWf5OWjR21ajxBCyD9T+O18LPwhHvnFFWLlBcUVWPhDfLsmj56enoiKisLp06dx/vx5REZGIj4+HgBw4sQJ9OjRAxs3bkR+fj53PFVWVhbs7OwwefJkJCcnIzQ0FFevXm3R5n/e3t6YPHkykpKS4ODggOnTp3NnUAO1u5sHBwcjJSUFO3fuxP79+8XO03ZwcECPHj0QFxeHmzdvYuXKldxSqLeJi3y4KHFsJ6qqquDz+Q12oSwsLGxwVl0dLS0tGBkZgc/nc2XGxsYoKCho8vw9ZWVlGBkZITMzs8lYVq1aheLiYu5x7969N3hFhLSfLmpqbVqPEELIP0+NiMHnlxQ0Nim1rsznl5R2mbZaWlqKQ4cOYdu2bRg1ahT69u2LoKAgbjmRiooK+Hw+BAIBNDU1ub/1/Pz84ODgAHd3dxgaGmLw4MHYtWsXDh8+jIqKiua6xJQpUzBv3jwYGRlh06ZNGDhwIHbv3s1dX7t2LQYPHgyhUIjx48dj2bJlYgMJeXl5sLGxQZ8+fWBoaIgpU6agf//+bx0X+XBR4thOpKSkYGFhgYsXL3JlIpEIFy9ehLW1daP3DBkyBJmZmWJHVqSnp0NLSwtSUlKN3lNWVoasrCyxw9brk5aWhqKiotiDkHeJ3EALdNHU5DbCaYDHQxdNTcgNtOjYwAghhLwzYrOfNhhpfBUDkF9cgdjstj+66e7du6iuruaO8gJqz1vu3bt3s/clJSUhODgYCgoK3MPW1hYikQjZ2dnw9fUVu/bqvhX1/160trYWG3EMDQ3FkCFDoKmpCQUFBaxdu1bsfk9PT8ybNw82Njb4+uuvkZWV1eK4CGkMJY7tyNPTE/v378ehQ4eQmpqKhQsXory8HHPnzgUAODo6YtWqVVz9hQsX4unTp1i6dCnS09Nx9uxZ+Pr6YtGiRVydZcuW4fLly8jJyUF0dDS++OIL8Pl8zJgxo8NfHyFthcfnQ2P13z8L9ZPHv59rrF4F3iuj8YQQQj4sD0tbNhLW0nodoaysDPPnz0diYiL3SEpKQkZGBgwMDLBgwQKxa9ra2i1q99q1a3BwcMDnn3+OM2fOICEhAWvWrBGbobZhwwbcuXMHY8eOxR9//AETExOcPHmyRXER0pgunR3Au8DT07PFdbdv397iutOmTcOjR4+wbt06FBQUwMzMDOHh4dyGOXl5eZCQ+F/urqOjg99++w0eHh4wNTVF9+7dsXTpUnh5eXF1/vrrL8yYMQNPnjyBmpoahg4diuvXr0ONpvCR95zimDHAzh0o9PUT2yini4YGNFavoqM4CCHkA6cuaNnxHS2t1xo9e/aEpKQk4uLioKurCwAoLi5Geno6t/eAlJRUg53wzc3NkZKSgl69ejXaroqKClRUVBq9dv36dTg6Ooo9HzBgAAAgOjoaenp6WLNmDXc9Nze3QRtGRkYwMjKCh4cHZsyYgaCgIHzxxRevjYuQxlDiCCAhIaFF9XhNTaNrhpubW5MLjSMiIhqUWVtb4/r16022FxIS0uoYCHlfKI4ZA8GoUbW7rD56hC5qapAbaEEjjYQQQmCprwItJRkUFFc0us6RB0BTSQaW+o0nYm9DIBDAyckJy5cvh4qKCtTV1bF+/XpISEhwfx8KhUJcuXIF06dPh7S0NFRVVeHl5YWPP/4Ybm5umDdvHuTl5ZGSkoLz589z53w35aeffsLAgQMxdOhQHD16FLGxsfj+++8BAIaGhsjLy0NISAgGDRqEs2fPcqOJAPDixQssX74c//rXv6Cvr4+//voLcXFxmDx5MgC8VVzkw0WJI4BLly51dgiEkL/x+HzIW1m+viIhhJAPCl+Ch/XjTbDwh3jwALHkse6j/fXjTcCXaP0H/S2xfft2LFiwAOPGjYOioiJWrFiBe/fucYepb9y4EfPnz4eBgQEqKyvBGIOpqSkuX76MNWvWYNiwYWCMwcDAANOmTXttfz4+PggJCcGXX34JLS0t/PjjjzAxMQEATJgwAR4eHnBzc0NlZSXGjh0Lb29vbNiwAQDA5/Px5MkTODo6orCwEKqqqpg0aRJ8fHwA4K3iIh8uHmONnbhN/slKSkqgpKSE4uJi2iiHEEIIIR2moqIC2dnZ0NfX5xKu1gq/nQ+fX1LENsrRUpLB+vEmsOvb9GaBba28vBzdu3eHv78/nJ2dO6xfQt5Ecz97Lc0NaMSxETdu3MB///tf5OXlNTgG48SJE50UFSGEEEIIseurhdEmmojNfoqHpRVQF9ROT22vkcY6CQkJ+PPPP2FpaYni4mJs3LgRAGBvb9+u/RLyrqBdVesJCQnB4MGDkZqaipMnT6K6uhp37tzBH3/8ASUlpc4OjxBCCCHkg8eX4MHaoBvszbrD2qBbuyeNdbZt24b+/fvDxsYG5eXliIyMhKqqaof0TUhnoxHHenx9fREQEIBFixZBIBBg586d0NfXx/z585s9K5EQQgghhPxzDRgwADdv3uzsMAjpNDTiWE9WVhbGjh0LoHZb5fLycvB4PHh4eGDfvn2dHB0hhBBCCCGEdDxKHOvp2rUrSktLAQDdu3fH7du3AQBFRUV4/vx5Z4ZGCCGEEEIIIZ2CpqrWM3z4cJw/fx79+vXDlClTsHTpUvzxxx84f/48Ro0a1dnhEUIIIYQQQkiHo8Sxnn//+9+oqKjd3nnNmjWQlJREdHQ0Jk+ejLVr13ZydIQQQgghhBDS8ShxrEdFRYX7WkJCAitXruzEaAghhBBCCCGk89Eax3psbGwQHByMkpKSzg6FEEIIIYQQQt4JlDjW89FHH2HVqlXQ1NTElClTcOrUKVRXV3d2WIQQQggh5APB4/EQFhbW2WEQIoYSx3p27tyJ+/fvIywsDPLy8nB0dISGhgZcXV1x+fLlzg6PEEIIIYS8g4RCIXbs2NHZYRDSbihxbISEhATGjBmD4OBgFBYWYu/evYiNjcWnn37a2aERQgghhBBRDZAdCdz6ufa/oprOjuidUlVV1dkhkH8gShybUVBQgMDAQGzduhXJyckYNGhQZ4dECCGEEPJhSzkN7OgLHBoHHHeu/e+OvrXl7ai0tBQODg6Ql5eHlpYWAgICMHLkSLi7u2PkyJHIzc2Fh4cHeDweeDwed9/Vq1cxbNgwyMrKQkdHB0uWLEF5eXmr+vby8oKRkRHk5OTQs2dPeHt7iy2l2rBhA8zMzHDgwAHo6+tDRkYGAPDnn39i6NChkJGRgYmJCS5cuNBgGuy9e/cwdepUKCsrQ0VFBfb29sjJyXmr94r8M1HiWE9JSQmCgoIwevRo6OjoYM+ePZgwYQIyMjJw/fr1zg6PEEIIIeTDlXIa+K8jUPJAvLwkv7a8HZNHT09PREVF4fTp0zh//jwiIyMRHx8PADhx4gR69OiBjRs3Ij8/H/n5+QCArKws2NnZYfLkyUhOTkZoaCiuXr0KNze3VvUtEAgQHByMlJQU7Ny5E/v370dAQIBYnczMTBw/fhwnTpxAYmIiampqMHHiRMjJySEmJgb79u3DmjVrxO6prq6Gra0tBAIBIiMjERUVBQUFBdjZ2dGoJWmAjuOoR0NDA127dsW0adPg5+eHgQMHdnZIhBBCCCFEVAOEewFgjVxkAHhA+Eqgz1hAgt+mXZeWluLQoUM4duwYRo0aBQAICgqCtrY2gNrj3Ph8PgQCATQ1Nbn7/Pz84ODgAHd3dwCAoaEhdu3ahREjRmDPnj3cyODrvHqWuFAoxLJlyxASEoIVK1Zw5VVVVTh8+DDU1NQAAOHh4cjKykJERAQX05YtWzB69GjuntDQUIhEIhw4cIAbJQ0KCoKysjIiIiIwZsyY1r5V5B+MEsd6Tp8+jVGjRkFCggZjCSGEEELeGbnRDUcaxTCg5H5tPf1hbdr13bt3UV1dDUtLS65MSUkJvXv3bva+pKQkJCcn4+jRo/+LkjGIRCJkZ2fj5MmT8PX15a6lpKRAV1e3QTuhoaHYtWsXsrKyUFZWhpcvX0JRUVGsjp6eHpc0AkBaWhp0dHTEEtlX46+LLzMzEwKBQKy8oqICWVlZzb428uGhxLGe0aNH4+XLl/jjjz+QlZWFmTNnQiAQ4MGDB1BUVISCgkJnh0gIIYQQ8uEpK2zbeh2grKwM8+fPx5IlSxpc09XVxYIFCzB16lSurG4E81XXrl2Dg4MDfHx8YGtrCyUlJYSEhMDf31+snry8/BvFZ2FhIZbY1nk1CSUEoMSxgdzcXNjZ2SEvLw+VlZUYPXo0BAIBtm7disrKSgQGBnZ2iIQQQgghHx4Fjbat1wo9e/aEpKQk4uLiuBHB4uJipKenY/jw4QAAKSkp1NSI7+5qbm6OlJQU9OrVq9F2VVRUoKKi0mzf0dHR0NPTE1ufmJub+9qYe/fujXv37qGwsBAaGrXvSVxcXIP4QkNDoa6u3mAEk5D6aD5mPUuXLsXAgQPx7NkzyMrKcuVffPEFLl682ImREUIIIYR8wPQGA4raAHhNVOABit1r67UxgUAAJycnLF++HJcuXcKdO3fg7OwMCQkJbm2gUCjElStXcP/+fTx+/BhA7W6o0dHRcHNzQ2JiIjIyMnDq1KlWbY5jaGiIvLw8hISEICsrC7t27cLJkydfe9/o0aNhYGAAJycnJCcnIyoqilsrWRezg4MDVFVVYW9vj8jISGRnZyMiIgJLlizBX3/91dq3ifzDUeJYT2RkJNauXQspKSmxcqFQiPv373dSVIQQQgghHzgJPmC39e8n9ZPHv5/bfd3mG+PU2b59O6ytrTFu3DjY2NhgyJAhMDY25ja42bhxI3JycmBgYMBN8zQ1NcXly5eRnp6OYcOGYcCAAVi3bl2jU1KbMmHCBHh4eMDNzQ1mZmaIjo6Gt7f3a+/j8/kICwtDWVkZBg0ahHnz5nGjlnUxy8nJ4cqVK9DV1cWkSZNgbGwMZ2dnVFRU0AgkaYDHGGtsa6oPVteuXREVFQUTExMIBAIkJSWhZ8+euHr1KiZPnozCwndn3vybKikpgZKSEoqLi+mXAiGEEEI6TEVFBbKzs8XOGmy1lNO1u6u+ulGOYvfapNFkQtsE2gLl5eXo3r07/P394ezs3GH9vo2oqCgMHToUmZmZMDAw6OxwSAdq7mevpbkBjTjWM2bMGOzYsYN7zuPxUFZWhvXr1+Pzzz9vdXvfffcdhEIhZGRkYGVlhdjY2GbrFxUVYdGiRdDS0oK0tDSMjIxw7ty5t2qTEEIIIeQfw2QC4H4bcDoDTP6+9r/ut9o9aUxISMCPP/6IrKwsxMfHw8HBAQBgb2/frv2+jZMnT+L8+fPIycnBhQsX4OrqiiFDhlDSSN4IbY5Tj7+/P2xtbWFiYoKKigrMnDkTGRkZUFVVxY8//tiqtkJDQ+Hp6YnAwEBYWVlhx44dsLW1RVpaGtTV1RvUr6qqwujRo6Guro6ff/4Z3bt3R25uLpSVld+4TUIIIYSQfxwJfpsfudES27ZtQ1paGqSkpGBhYYHIyEioqqp2eBwtVVpaCi8vL+Tl5UFVVRU2NjYNdmMlpKVoqmojXr58iZCQECQnJ6OsrAzm5uZwcHAQ2yynJaysrDBo0CD8+9//BgCIRCLo6Ohg8eLFWLlyZYP6gYGB+Pbbb/Hnn39CUlKyTdpsDE1VJYQQQkhnaJOpqoSQVmuLqao04tiILl26YNasWW/VRlVVFW7evIlVq1ZxZRISErCxscG1a9cavef06dOwtrbGokWLcOrUKaipqWHmzJnw8vICn89/ozYBoLKyEpWVldzzkpKSt3pthBBCCCGEkA8LJY6oTdg+++wzSEpK4vTp083WnTChZfPnHz9+jJqaGu7cnDoaGhr4888/G73n7t27+OOPP+Dg4IBz584hMzMTX375Jaqrq7F+/fo3ahMA/Pz84OPj06K4CSGEEEIIIaQ+ShwBTJw4EQUFBVBXV8fEiRObrMfj8Roc7NqWRCIR1NXVsW/fPvD5fFhYWOD+/fv49ttvsX79+jdud9WqVfD09OSel5SUQEdHpy1CJoQQQgghhHwAKHFEbcLW2NdvQ1VVFXw+v8HxHYWFhdDU1Gz0Hi0tLUhKSoLP/9/5Q8bGxigoKEBVVdUbtQkA0tLSkJaWfotXQwghhBBCCPmQ0XEcr6iursaoUaOQkZHx1m3V7bZ18eJFrkwkEuHixYuwtrZu9J4hQ4YgMzNTLHlNT0+HlpYWpKSk3qhNQgghhBBCCHlblDi+QlJSEsnJyW3WnqenJ/bv349Dhw4hNTUVCxcuRHl5OebOnQsAcHR0FNvoZuHChXj69CmWLl2K9PR0nD17Fr6+vli0aFGL2ySEEEIIIW1r5MiRcHd37/B+58yZ0+wyKkI6Ek1VrWfWrFn4/vvv8fXXX791W9OmTcOjR4+wbt06FBQUwMzMDOHh4dzmNnl5eZCQ+F/urqOjg99++w0eHh4wNTVF9+7dsXTpUnh5ebW4TUIIIYQQQghpa5Q41vPy5UscPHgQFy5cgIWFBeTl5cWub9++vVXtubm5wc3NrdFrERERDcqsra1x/fr1N26TEEIIIYSQOtXV1U2eD05Ia9BU1Xpu374Nc3NzCAQCpKenIyEhQexBCCGEEEI6V42oBnEFcTh39xziCuJQI2q/Xe/rvHz5Em5ublBSUoKqqiq8vb3BGAMA5OfnY+zYsZCVlYW+vj6OHTsGoVCIHTt2cPcXFRVh3rx5UFNTg6KiIj799FMkJSW1Kobw8HAMHToUysrK6NatG8aNG4esrCzuek5ODng8HkJDQzFixAjIyMjg6NGjePnyJZYsWcLd5+XlBScnJ7FpsCKRCH5+ftDX14esrCz69++Pn3/++bUxXb16FcOGDYOsrCx0dHSwZMkSlJeXc9eFQiF8fX3xf//3fxAIBNDV1cW+ffta9brJu4FGHOu5dOlSZ4dACCGEEEKacCH3Ar6O/RqFz/+3y7yGnAZWWq6EjZ5Nu/V76NAhODs7IzY2Fjdu3ICrqyt0dXXh4uICR0dHPH78GBEREZCUlISnpycePnwodv+UKVMgKyuLX3/9FUpKSti7dy9GjRqF9PR0qKiotCiG8vJyeHp6wtTUFGVlZVi3bh2++OILJCYmii1/WrlyJfz9/TFgwADIyMhg69atOHr0KIKCgmBsbIydO3ciLCwMn3zyCXePn58ffvjhBwQGBsLQ0BBXrlzBrFmzoKamhhEjRjQaT1ZWFuzs7LB582YcPHgQjx494mbGBQUFcfX8/f2xadMmrF69Gj///DMWLlyIESNGoHfv3q35FpDOxoiYuXPnspKSkgblZWVlbO7cuZ0QUdsrLi5mAFhxcXFnh0IIIYSQD8iLFy9YSkoKe/HixRvdfz7nPOsX3I/1De4r9ugX3I/1C+7Hzuecb+OIa40YMYIZGxszkUjElXl5eTFjY2OWmprKALC4uDjuWkZGBgPAAgICGGOMRUZGMkVFRVZRUSHWroGBAdu7d2+T/To5OTF7e/smrz969IgBYLdu3WKMMZadnc0AsB07dojV09DQYN9++y33/OXLl0xXV5dru6KigsnJybHo6Gix+5ydndmMGTOa7N/Z2Zm5urqKlUVGRjIJCQnue6ynp8dmzZrFXReJRExdXZ3t2bOnyXZJ22vuZ6+luQFNVa3n0KFDePHiRYPyFy9e4PDhw50QESGEEEIIqRHV4OvYr8HAGlyrK9sau7Xdpq1+/PHH4PF43HNra2tkZGQgLS0NXbp0gbm5OXetV69e6Nq1K/c8KSkJZWVl6NatGxQUFLhHdnY2srKykJeXJ1bu6+vbaAwZGRmYMWMGevbsCUVFRQiFQgC1Gy6+auDAgdzXxcXFKCwshKWlJVfG5/NhYWHBPc/MzMTz588xevRosTgOHz7MTYX96KOPuPLPPvuMe13BwcFi99ja2kIkEiE7O5tr39TUlPuax+NBU1OzwYgseffRVNW/lZSUgDEGxhhKS0shIyPDXaupqcG5c+egrq7eiRESQgghhHy44h/Gi01PrY+BoeB5AeIfxmOQ5qAOjOz1ysrKoKWl1ejGiMrKylBWVkZiYiJX1tTU1fHjx0NPTw/79++HtrY2RCIR+vbti6qqKrF69Td3bEl8AHD27Fl0795d7Jq0tDQA4Ny5c6iurgYAyMrKcvfNnz8fS5YsadCmrq4u93X9zXl4PJ7YueXk/UCJ49+UlZXB4/HA4/FgZGTU4DqPx4OPj08nREYIIYQQQh49f9Sm9VorJiZG7Pn169dhaGiI3r174+XLl0hISOBG8TIzM/Hs2TOurrm5OQoKCtClSxdulLC+Xr16Ndv/kydPkJaWhv3792PYsGEAajemeR0lJSVoaGggLi4Ow4cPB1A7KBIfHw8zMzMAgImJCaSlpZGXl9fkekY9Pb0GZebm5khJSXlt7OSfgRLHv126dAmMMXz66ac4fvy42Cc9UlJS0NPTg7a2didGSAghhBDy4VKTU2vTeq2Vl5cHT09PzJ8/H/Hx8di9ezf8/f3Rp08f2NjYwNXVFXv27IGkpCS++uoryMrKclNbbWxsYG1tjYkTJ+Kbb76BkZERHjx4gLNnz+KLL74Qm1ralK5du6Jbt27Yt28ftLS0kJeXh5UrV7Yo9sWLF8PPzw+9evVCnz59sHv3bjx79oyLTyAQYNmyZfDw8IBIJMLQoUNRXFyMqKgoKCoqwsnJqdF2vby88PHHH8PNzQ3z5s2DvLw8UlJScP78efz73/9u4TtL3heUOP6t7tOV7Oxs6Orqis1hJ4QQQgghnctc3Rwachp4+Pxho+sceeBBQ04D5urmjdz99hwdHfHixQtYWlqCz+dj6dKlcHV1BQAcPnwYzs7OGD58ODQ1NeHn54c7d+5wS594PB7OnTuHNWvWYO7cuXj06BE0NTUxfPhwaGhotKh/CQkJhISEYMmSJejbty969+6NXbt2YeTIka+918vLCwUFBXB0dASfz4erqytsbW3B5/O5Ops2bYKamhr8/Pxw9+5dKCsrw9zcHKtXr26yXVNTU1y+fBlr1qzBsGHDwBiDgYEBpk2b1qLXRN4vPMZYw5+8D1xkZCT27t2Lu3fv4qeffkL37t1x5MgR6OvrY+jQoZ0d3lsrKSmBkpISiouLoaio2NnhEEIIIeQDUVFRgezsbOjr64vtJ9FSF3IvwDPCEwDEkkceaj/w3z5ye7seydFSf/31F3R0dHDhwgWMGjWqs8NpQCQSwdjYGFOnTsWmTZs6OxzSAZr72WtpbkC7qtZz/Phx2NraQlZWFvHx8aisrARQuyNVUztcEUIIIYSQ9mejZ4PtI7dDXU58w0INOY1OTRr/+OMPnD59GtnZ2YiOjsb06dMhFAq5NYWdLTc3F/v370d6ejpu3bqFhQsXIjs7GzNnzuzs0Mh7hKaq1rN582YEBgbC0dERISEhXPmQIUOwefPmToyMEEIIIYTY6NngE51PEP8wHo+eP4KanBrM1c3Bl+C//uZ2Ul1djdWrV+Pu3bsQCAQYPHgwjh492mA30c4iISGB4OBgLFu2DIwx9O3bFxcuXICxsXFnh0beI5Q41pOWltbop0NKSkooKirq+IAIIYQQQogYvgT/nTpyw9bWFra2tp0dRpN0dHQQFRXV2WGQ9xxNVa1HU1MTmZmZDcqvXr2Knj17dkJEhBBCCCGEENK5KHGsx8XFBUuXLkVMTAx4PB4ePHiAo0ePYtmyZVi4cGFnh0cIIYQQQgghHY6mqtazcuVKiEQijBo1Cs+fP8fw4cMhLS2NZcuWYfHixZ0dHiGEEEIIIYR0OEoc6+HxeFizZg2WL1+OzMxMlJWVwcTEBAoKCnjx4gVkZWU7O0RCCCGEEEII6VA0VbUJUlJSMDExgaWlJSQlJbF9+3bo6+t3dliEEEIIIYQQ0uEocfxbZWUlVq1ahYEDB2Lw4MEICwsDAAQFBUFfXx8BAQHw8PDo3CAJIYQQQgghpBNQ4vi3devWYc+ePRAKhcjJycGUKVPg6uqKgIAAbN++HTk5OfDy8ursMAkhhBBCCHlrwcHBUFZWbpe2eTweNwjTmJycHPB4PCQmJgIAIiIiwOPx3tmj7971+DoKJY5/++mnn3D48GH8/PPP+P3331FTU4OXL18iKSkJ06dPB5/feYfKEkIIIYSQzjNy5Ei4u7u/822+rwYPHoz8/HwoKSl1dijtijGGdevWQUtLC7KysrCxsUFGRoZYnadPn8LBwQGKiopQVlaGs7MzysrKuOsRERGwt7eHlpYW5OXlYWZmhqNHj3ZI/JQ4/u2vv/6ChYUFAKBv376QlpaGh4cHeDxeJ0dGCCGEEELIP5eUlBQ0NTX/8X93f/PNN9i1axcCAwMRExMDeXl52NraoqKigqvj4OCAO3fu4Pz58zhz5gyuXLkCV1dX7np0dDRMTU1x/PhxJCcnY+7cuXB0dMSZM2faPX5KHP9WU1MDKSkp7nmXLl2goKDQiRERQgghhJDGsJoalMfEovjMWZTHxILV1LRbX3PmzMHly5exc+dO8Hg88Hg85OTk4Pbt2/jss8+goKAADQ0NzJ49G48fPwZQOyokJSWFyMhIrp1vvvkG6urqKCwsbLLNply9ehXDhg2DrKwsdHR0sGTJEpSXl3PXhUIhNm/eDEdHRygoKEBPTw+nT5/Go0ePYG9vDwUFBZiamuLGjRsN2g4LC4OhoSFkZGRga2uLe/fuiV0/deoUzM3NISMjg549e8LHxwcvX77krmdkZGD48OGQkZGBiYkJzp8/36CP2NhYDBgwADIyMhg4cCASEhLErtefClo3jfa3336DsbExFBQUYGdnh/z8fO6ely9fYsmSJVBWVka3bt3g5eUFJycnTJw4scn3EQCOHDmCgQMHQiAQQFNTEzNnzsTDhw/F6pw7dw5GRkaQlZXFJ5980uB78+TJE8yYMQPdu3eHnJwc+vXrhx9//LHZfhlj2LFjB9auXQt7e3uYmpri8OHDePDgATetNzU1FeHh4Thw4ACsrKwwdOhQ7N69GyEhIXjw4AEAYPXq1di0aRMGDx4MAwMDLF26FHZ2djhx4kSz/bcFShz/xhjDnDlzMGnSJEyaNAkVFRVYsGAB97zu0VrfffcdhEIhZGRkYGVlhdjY2CbrBgcHc7886h4yMjJidebMmdOgjp2dXavjIoQQQgh5H5X8/jsyR9kgz8kJD5YtQ56TEzJH2aDk99/bpb+dO3fC2toaLi4uyM/PR35+PgQCAT799FMMGDAAN27cQHh4OAoLCzF16lQA/5uGOnv2bBQXFyMhIQHe3t44cOAANDQ0Gm1TR0en0f6zsrJgZ2eHyZMnIzk5GaGhobh69Src3NzE6gUEBGDIkCFISEjA2LFjMXv2bDg6OmLWrFmIj4+HgYEBHB0dwRjj7nn+/Dm2bNmCw4cPIyoqCkVFRZg+fTp3PTIyEo6Ojli6dClSUlKwd+9eBAcHY8uWLQAAkUiESZMmQUpKCjExMQgMDGywJ0hZWRnGjRsHExMT3Lx5Exs2bMCyZcte+74/f/4c27Ztw5EjR3DlyhXk5eWJ3bd161YcPXoUQUFBiIqKQklJSbPrKutUV1dj06ZNSEpKQlhYGHJycjBnzhzu+r179zBp0iSMHz8eiYmJmDdvHlauXCnWRkVFBSwsLHD27Fncvn0brq6umD17drN/52dnZ6OgoAA2NjZcmZKSEqysrHDt2jUAwLVr16CsrIyBAwdydWxsbCAhIYGYmJgm2y4uLoaKisprX/tbY4QxxticOXNa9GiNkJAQJiUlxQ4ePMju3LnDXFxcmLKyMissLGy0flBQEFNUVGT5+fnco6CgQKyOk5MTs7OzE6vz9OnTVsVVXFzMALDi4uJW3UcIIYQQ8jZevHjBUlJS2IsXL97o/uLffmMpfYxZSu8+4o8+xiyljzEr/u23No641ogRI9jSpUu555s2bWJjxowRq3Pv3j0GgKWlpTHGGKusrGRmZmZs6tSpzMTEhLm4uDTbZlOcnZ2Zq6urWFlkZCSTkJDg3kc9PT02a9Ys7np+fj4DwLy9vbmya9euMQAsPz+fMVb7dycAdv36da5OamoqA8BiYmIYY4yNGjWK+fr6ivV95MgRpqWlxRhj7LfffmNdunRh9+/f567/+uuvDAA7efIkY4yxvXv3sm7duol9z/fs2cMAsISEBMYYY5cuXWIA2LNnz8Riy8zM5O757rvvmIaGBvdcQ0ODffvtt9zzly9fMl1dXWZvb9/Mu9lQXFwcA8BKS0sZY4ytWrWKmZiYiNXx8vISi68xY8eOZV999VWT16OiohgA9uDBA7HyKVOmsKlTpzLGGNuyZQszMjJqcK+amhr7z3/+02i7oaGhTEpKit2+fbvJvhlr/mevpblBl/ZPTd8PQUFBbd7m9u3b4eLigrlz5wIAAgMDcfbsWRw8eLDBJxd1eDweNDU1m21XWlr6tXUIIYQQQv5JWE0NCn39gFdGzP53kQE8Hgp9/SAYNQq8dt7UMCkpCZcuXWp0WVNWVhaMjIwgJSWFo0ePwtTUFHp6eggICHhtux999BFyc3MBAMOGDcOvv/6KpKQkJCcni22AwhiDSCRCdnY2jI2NAQCmpqbcdQ0NDQBAv379GpQ9fPiQ+zuyS5cuGDRoEFenT58+UFZWRmpqKiwtLZGUlISoqChuhBGoXd5VUVGB58+fIzU1FTo6OtDW1uauW1tbi72m1NRUmJqais2iq1+nMXJycjAwMOCea2lpcVNKi4uLUVhYCEtLS+46n8+HhYUFRCIRAODo0aOYP38+d/3XX3/FsGHDuFHPpKQkPHv2jKufl5cHExMTpKamwsrKSiyW+vHW1NTA19cX//3vf3H//n1UVVWhsrIScnJyTfbdHhttXrp0CXPnzsX+/fvx0UcftXn79VHi2E6qqqpw8+ZNrFq1iiuTkJCAjY0NNxzdmLKyMujp6UEkEsHc3By+vr4N/iFERERAXV0dXbt2xaefforNmzejW7duTbZZWVmJyspK7nlJSclbvDJCCCGEkI73/MZNvCwoaLoCY3hZUIDnN25C3sqy6XptoKysDOPHj8fWrVsbXNPS0uK+jo6OBlC7U+bTp08hLy/fbLvnzp1DdXU1AEBWVpbra/78+ViyZEmD+rq6utzXkpKS3Nd1m8w0VlaXKLVEWVkZfHx8Gl2uVX85VVt7NXagNn7W2IcGTZgwYYJYAti9e3eUl5fD1tYWtra2OHr0KNTU1JCXlwdbW1tUVVW1uO1vv/0WO3fuxI4dO9CvXz/Iy8vD3d2da6OxvuvWZxYWFor9GyksLISZmRkAQFNTs8F6y5cvX+Lp06cNBo0uX76M8ePHIyAgAI6Oji2O/W1Q4thOHj9+jJqaGu7TnToaGhr4888/G72nd+/eOHjwIExNTVFcXIxt27Zh8ODBuHPnDnr06AEAsLOzw6RJk6Cvr4+srCysXr0an332Ga5du9bkJxl+fn7w8fFp2xdICCGEENKBXj561Kb1WkNKSgo1r2zAY25ujuPHj0MoFKJLl8b/nM7KyoKHhwf279+P0NBQODk54cKFC5CQkGi0TQDQ09Nr0I65uTlSUlLQq1evNnxFtV6+fIkbN25wI3dpaWkoKiriRjHNzc2RlpbWZN/Gxsa4d+8e8vPzuWTo+vXrDeocOXIEFRUVXLJZv05rKSkpQUNDA3FxcRg+fDiA2lHA+Ph4LgkTCAQQCARi9928eRNPnjzB119/za0prb9hkLGxMU6fPi1WVj/eqKgo2NvbY9asWQBqk/H09HSYmJg02be+vj40NTVx8eJFLsaSkhLExMRg4cKFAGpHNouKinDz5k3utIc//vgDIpFILBGNiIjAuHHjsHXrVrEdV9sbbY7zDrG2toajoyPMzMwwYsQInDhxAmpqati7dy9XZ/r06ZgwYQL69euHiRMn4syZM4iLi0NEREST7a5atQrFxcXco/5uWYQQQggh77ouamptWq81hEIhYmJikJOTg8ePH2PRokV4+vQpZsyYgbi4OGRlZeG3337D3LlzUVNTg5qaGsyaNQu2traYO3cugoKCkJycDH9//ybbbGok0MvLC9HR0XBzc0NiYiIyMjJw6tSpBpvjvAlJSUksXrwYMTExuHnzJubMmYOPP/6YSyTXrVuHw4cPw8fHB3fu3EFqaipCQkKwdu1aALUbtxgZGcHJyQlJSUmIjIzEmjVrxPqYOXMmeDweXFxckJKSgnPnzmHbtm1vHfvixYvh5+eHU6dOIS0tDUuXLsWzZ8+aPdJDV1cXUlJS2L17N+7evYvTp09j06ZNYnUWLFiAjIwMLF++HGlpaTh27BiCg4PF6hgaGuL8+fOIjo5Gamoq5s+fj8LCwmbj5fF4cHd3x+bNm3H69GncunULjo6O0NbW5naCNTY2hp2dHVxcXBAbG4uoqCi4ublh+vTp3HTgS5cuYezYsViyZAkmT56MgoICFBQU4OnTp61/E1uJEsd2oqqqCj6f3+AfUWFhYYvXJ0pKSmLAgAHIzMxssk7Pnj2hqqrabB1paWkoKiqKPQghhBBC3idyAy3QRVMTaCox4PHQRVMTcgMt2rzvZcuWgc/nw8TEBGpqaqiqqkJUVBRqamowZswY9OvXD+7u7lBWVoaEhAS2bNmC3Nxc7sN/LS0t7Nu3D2vXrkVSUlKjbebl5TXat6mpKS5fvoz09HQMGzYMAwYMwLp168TWFb4pOTk5eHl5YebMmRgyZAgUFBQQGhrKXbe1tcWZM2fw+++/Y9CgQfj4448REBDAjYxKSEjg5MmTePHiBSwtLTFv3jyx9ZAAoKCggF9++QW3bt3CgAEDsGbNmkan+LaWl5cXZsyYAUdHR1hbW0NBQQG2trbNTqFVU1NDcHAwfvrpJ5iYmODrr79ukMTq6uri+PHjCAsLQ//+/REYGAhfX1+xOmvXroW5uTlsbW0xcuRIaGpqvvYYEABYsWIFFi9eDFdXVwwaNAhlZWUIDw8Xi/no0aPo06cPRo0ahc8//xxDhw7Fvn37uOuHDh3C8+fP4efnBy0tLe7xJqc/tBaPtWayMGkVKysrWFpaYvfu3QBqh7F1dXXh5ubW5OY4r6qpqcFHH32Ezz//HNu3b2+0zl9//QVdXV2EhYVhwoQJLYqrpKQESkpKKC4upiSSEEIIIR2moqIC2dnZ0NfXf6M1ciW//477S91rn7z6J+zfyWT3nTugOGZMG0RK3jcikQjGxsaYOnVqg1FE0vzPXktzAxpxbEeenp7Yv38/Dh06hNTUVCxcuBDl5eXcLquOjo5im+ds3LgRv//+O+7evYv4+HjMmjULubm5mDdvHoDaBcrLly/H9evXkZOTg4sXL8Le3h69evWCra1tp7xGQgghhJCOojhmDLrv3IEu9faQ6KKhQUnjByY3Nxf79+9Heno6bt26hYULFyI7OxszZ87s7ND+sWhznHY0bdo0PHr0COvWrUNBQQHMzMwQHh7ObZiTl5fHLZAGgGfPnsHFxQUFBQXo2rUrLCwsEB0dzS205fP5SE5OxqFDh1BUVARtbW2MGTMGmzZtgrS0dKe8RkIIIYSQjqQ4ZgwEo0bV7rL66BG6qKlBbqBFux/BQd4tEhISCA4OxrJly8AYQ9++fXHhwgVuYx/S9miq6geIpqoSQgghpDO87VRVQsiboamqhBBCCCGEEELaHSWOhBBCCCGEEEKaRYkjIYQQQgghhJBmvdOJI4/HQ1hYWGeH0STGGFxdXaGiogIej4fExESMHDkS7u7unR1ah4uIiACPx0NRUVGz9fbt2wcdHR1ISEhgx44dHRIbIYQQQggh5O20+a6qQqEQ7u7uH0TyFB4ejuDgYERERKBnz55QVVXFiRMnICkp2e59jxw5EmZmZu9V8lVSUgI3Nzds374dkydPhpKSUmeHRAghhBBCCGmBf/RxHFVVVZCSkmq39rOysqClpYXBgwdzZSoqKu3W3/suLy8P1dXVGDt2LLS0tDo7HEIIIYQQQkgLtXqqamlpKRwcHCAvLw8tLS0EBARw0zNHjhyJ3NxceHh4gMfjgcfjcfddvXoVw4YNg6ysLHR0dLBkyRKUl5e3qm8vLy8YGRlBTk4OPXv2hLe3N6qrq7nrGzZsgJmZGQ4cOCC21eyff/6JoUOHQkZGBiYmJrhw4UKDabD37t3D1KlToaysDBUVFdjb2yMnJ6fJWObMmYPFixcjLy8PPB4PQqEQABpMVRUKhfD19cX//d//QSAQQFdXF/v27RNr6036vnz5Mnbu3Mm9zzk5OQgODoaysrJY3bCwMLHvw4YNGzB06FAAQL9+/aCkpITp06ejtLSUqyMSieDn5wd9fX3Iysqif//++Pnnn8XaPXfuHIyMjCArK4tPPvmk2XgBIDg4GP369QMA9OzZk4u57nu2d+9e6OjoQE5ODlOnTkVxcXGz7RFCCCGE/FPRcq33x4e0XKvViaOnpyeioqJw+vRpnD9/HpGRkYiPjwcAnDhxAj169MDGjRuRn5+P/Px8ALUjc3Z2dpg8eTKSk5MRGhqKq1evws3NrVV9CwQCBAcHIyUlBTt37sT+/fsREBAgViczMxPHjx/HiRMnkJiYiJqaGkycOBFycnKIiYnBvn37sGbNGrF7qqurYWtrC4FAgMjISERFRUFBQQF2dnaoqqpqNJadO3di48aN6NGjB/Lz8xEXF9dk3P7+/hg4cCASEhLw5ZdfYuHChUhLS3urvq2treHi4sK9zzo6Oi1+H7OzswEAoaGhOHPmDC5fvoyvv/6au+7n54fDhw8jMDAQd+7cgYeHB2bNmoXLly8DqE10J02ahPHjxyMxMRHz5s3DypUrm+1z2rRpuHDhAgAgNjZWLObMzEz897//xS+//ILw8HDufSKEEEIIeV8IhcL3NiForbrlWmfOnEF+fj769u2LEydOYNOmTe3e9/uYoNYt1/Ly8sL9+/fh6ura2SG9kVZNVS0tLcWhQ4dw7NgxjBo1CgAQFBQEbW1tALXTNPl8PgQCATQ1Nbn7/Pz84ODgwH2TDQ0NsWvXLowYMQJ79uxp8QGwa9eu5b4WCoVYtmwZQkJCsGLFCq68qqoKhw8fhpqaGoDaf9hZWVmIiIjgYtqyZQtGjx7N3RMaGgqRSIQDBw5wo3NBQUFQVlZGREQExowZ0yAWJSUlCAQC8Pl8sdfamM8//5xLhLy8vBAQEIBLly6hd+/eb9y3lJQU5OTkXtt3Y0QiEQDAxMQEioqKmD17Ni5evIgtW7agsrISvr6+uHDhAqytrQHUjhBevXoVe/fu5b5nBgYG8Pf3BwD07t0bt27dwtatW5vsU1ZWFt26dQMAqKmpicVdUVGBw4cPo3v37gCA3bt3Y+zYsfD393+j10cIIYQQ8iGj5Vrvln/Kcq1WjTjevXsX1dXVsLS05MqUlJTQu3fvZu9LSkpCcHAwFBQUuIetrS1EIhGys7Ph6+srdi0vL6/RdkJDQzFkyBBoampCQUEBa9eubVBXT0+PSxoBIC0tDTo6OmIJyKvx18WXmZkJgUDAxaCiooKKigpkZWUhMjJSLL6jR4+2+D0DAFNTU+5rHo8HTU1NPHz4sEP6boyurq7Ycy0tLS6ezMxMPH/+HKNHjxbr9/Dhw8jKygIApKamwsrKSqyNuiSzzqv3Lliw4LXx1CWNdW2JRCJuVJYQQggh5FUiEcP9tGdIjyvA/bRnEIlYu/dJy7Vqvc/LtczMzHDkyBEIhUJarvUGOmRznLKyMsyfPx9LlixpcE1XVxcLFizA1KlTubK6EcxXXbt2DQ4ODvDx8YGtrS2UlJQQEhLCjXrVkZeXf6P4LCwsGk3K1NTUICUlhcTERK5MQ0OjVe3X32WVx+Nxo35t2beEhAQYE//F+eovlZbGAwBnz54VS+YAQFpausm+63s1ZkVFxRbfRwghhBDSnKyEh4gMzUB5USVXJq8sjWHTDGEwQL3d+n11uZaGhgbWrVuH+Ph4mJmZ4cSJE+jfvz9cXV3h4uLyv1j/Xq61efNmHDx4EI8ePYKbmxvc3NwQFBTU4r7rlmtpa2vj1q1bcHFxgUAgEJt19+pyLT6fzy3X0tXVRUxMDEpLS/HVV1+JtVu3ZMra2hqRkZHo0qULNm/eDDs7OyQnJzc6arlz504YGBhg3759iIuLA5/PbzJuf39/bNq0CatXr8bPP/+MhQsXYsSIEejdu/cb952eno6+ffti48aNACA2YPQ6WVlZCAsLw5kzZ/Ds2TNMnToVX3/9NbZs2QKgdpbkDz/8gMDAQBgaGuLKlSuYNWsW1NTUMGLECG651qJFi+Dq6oobN240eE/rmzZtGnR0dGBjY4PY2Fjo6OhwMb+6XKukpATOzs748ssv22SgqD20KnHs2bMnJCUlERcXx41aFRcXIz09HcOHDwcASElJoaamRuw+c3NzpKSkoFevXo22q6Ki8trh7ejoaOjp6YmtT8zNzX1tzL1798a9e/dQWFjIJV311yOam5sjNDQU6urqTSY5TcX+tt6078beZzU1NZSWlqK8vJxLoF9N4FrCxMQE0tLSyMvLw4gRIxqtY2xsjNOnT4uVXb9+/bUxNyUvLw8PHjzgPjC4fv06JCQkXjuSTQghhJAPS1bCQ4Tvvd2gvLyoEuF7b8Nuft92SR5pudb/vO/LtYKDgyEQCACAlmu1UqumqgoEAjg5OWH58uW4dOkS7ty5A2dnZ0hISHDfcKFQiCtXruD+/ft4/PgxgNp/KNHR0XBzc0NiYiIyMjJw6tSpVm2OY2hoiLy8PISEhCArKwu7du3CyZMnX3vf6NGjYWBgACcnJyQnJyMqKor74auL2cHBAaqqqrC3t0dkZCSys7MRERGBJUuW4K+//mrNW9Rqb9q3UChETEwMcnJy8PjxY4hEIlhZWUFOTg6rV69GVlYWjh07huDg4FbFIxAIsGzZMnh4eODQoUPIyspCfHw8du/ejUOHDgEAFixYgIyMDCxfvhxpaWlv1A9QO82k5PELSEtJY/qUmUhISERkZCSWLFmCqVOnvpM/MIQQQgjpHCIRQ2RoRrN1rv43o12mrdJyrX/Gci2hUMgljQAt12qtVk9V3b59OxYsWIBx48ZBUVERK1aswL1797hPTDZu3Ij58+fDwMAAlZWVYIzB1NQUly9fxpo1azBs2DAwxmBgYIBp06a1uN8JEybAw8MDbm5uqKysxNixY+Ht7Y0NGzY0ex+fz0dYWBjmzZuHQYMGoWfPnvj2228xfvx4LmY5OTlcuXIFXl5emDRpEkpLS9G9e3eMGjWq3adZvmnfy5Ytg5OTE0xMTPDixQtkZ2dDKBTihx9+wPLly7F//36MGjUKGzZsaPXOTZs2bYKamhr8/Pxw9+5dKCsrw9zcHKtXrwZQ+4/8+PHj8PDwwO7du2FpacnNYW+pumkmf14vQFc5LWhLmOHT4aPxoroM48ePw3/+859WxUwIIYSQf7b8jCKx6amNKXtWifyMInTv3bWDomoeLdei5Vr/JK1OHAUCgdg3uLy8HD4+Plxy8vHHHyMpKanBfYMGDcLvv//eqr7q/wP45ptv8M0334iVvboId8OGDY0mkn369MHVq1e551FRUQDEp1NqampyI2ot5e7u3mA74IiICLHnjS2YrT999E36NjIywrVr1xqUT5w4ERMnThQre3Wu/YYNG+Dp6QklJSWurP7r4PF4WLp0KZYuXdpk/+PGjcO4cePEyubOndtszGZmZmCMNTrNZNhHEzDsowkAALv5fdG167vxC58QQggh74bykuaTxtbWaw1artX2aLnW+7dcq9XnOCYkJODHH3/kpjA6ODgAAOzt7ds8uLZy8uRJnD9/Hjk5Obhw4QJcXV0xZMgQGBgYdHZoH5zOnGZCCCGEkPeXvGLLRn1aWq81aLlW2/uQl2vVkZGRgZOTE5KSkt6L5VqtThwBYNu2bejfvz9sbGxQXl6OyMhIqKqqtnVsbaa0tBSLFi1Cnz59MGfOHAwaNAinTp3q7LA+SK2ZZkIIIYQQUkfLUBnyys0nhQpdpaFlqNwu/W/fvh3W1tYYN24cbGxsMGTIEBgbG4st18rJyYGBgQG31rBuuVZ6ejqGDRuGAQMGYN26dY1OSW3Kq8u1zMzMEB0dDW9v79feV7dcq6ysDIMGDcK8efO4Ucv6y7V0dXUxadIkGBsbw9nZGRUVFR22XKu1fS9btgx8Ph8mJiZQU1NDXl4eVFRU8MMPP+DcuXPo168ffvzxx9cuZ2vMpk2b4O3tDT8/PxgbG8POzg5nz56Fvr4+gP8t1woLC0P//v0RGBgIX1/fN30L0KtXL0yaNAmff/45xowZA1NT03d6uRaP1Z8PSv7xSkpKoKSkhOLi4g6fe50eV4Dz36e8tt5oZxMYDXo3P20hhBBCyJupqKhAdna22FmDrdHUrqp12mtX1caUl5eje/fu8Pf3h7Ozc4f0+baioqIwdOhQZGZm0sy7TrZhwwaEhYW1ekrtm2ruZ6+luUGHnONISJ3OnGZCCCGEkPebwQB12M3v2+AcR4Wu0hg6tX3PcUxISMCff/4JS0tLFBcXc+cIvuvLtRQUFGBoaIjMzEwsXbqUlmuRN0aJI+lQddNMmpuu2p7TTAghhBDyfjMYoA79/mq1y19KKiGvWPt3g4QEr9373rZtG9LS0iAlJQULC4v3YrmWl5cX8vLyoKqqChsbmwa7sRLSUjRV9QPUmVNVgXdrmgkhhBBCOs7bTlUlhLyZtpiq+kab4xDyNuqmmdRf4K7QVZqSRkIIIYQQQt5BlDi2s++++w5CoRAyMjKwsrJCbGxsk3WDg4PB4/HEHvU/EWCMYd26ddDS0oKsrCxsbGyQkdH88RbvIoMB6nD0HYyJHgMw2tkEEz0GYPaWwZQ0EkIIIYQQ8g6ixLEdhYaGwtPTE+vXr0d8fDz69+8PW1tbPHz4sMl7FBUVkZ+fzz3qH/D6zTffYNeuXQgMDERMTAzk5eVha2uLioqK9n45bU5CgofuvbvCaJAmuvfu2iFrEwghhBBCCCGtR4ljO9q+fTtcXFwwd+5cmJiYIDAwEHJycjh48GCT9/B4PGhqanIPDQ0N7hpjDDt27MDatWthb28PU1NTHD58GA8ePEBYWFgHvCJCCCGEEELIh4gSx3ZSVVWFmzdvwsbGhiuTkJCAjY0Nrl271uR9ZWVl0NPTg46ODuzt7XHnzh3uWnZ2NgoKCsTaVFJSgpWVVbNtVlZWoqSkROxBCCGEEEIIIS1FiWM7efz4MWpqasRGDAFAQ0MDBQUFjd7Tu3dvHDx4EKdOncIPP/wAkUiEwYMH46+//gIA7r7WtAkAfn5+UFJS4h46Ojpv89IIIYQQQgghHxhKHN8h1tbWcHR0hJmZGUaMGIETJ05ATU0Ne/fufat2V61aheLiYu5x7969NoqYEEIIIYS0NR6P904vQ2KMwdXVFSoqKuDxeEhMTMTIkSPh7u7e2aF1uIiICPB4PBQVFb1VO+/69xygxLHdqKqqgs/no7CwUKy8sLAQmpqaLWpDUlISAwYMQGZmJgBw97W2TWlpaSgqKoo9CCGEEEJI2xEKhdixY0dnh9EhwsPDERwcjDNnziA/Px99+/bFiRMnsGnTpnbv+0NNUN8FlDi2EykpKVhYWODixYtcmUgkwsWLF2Ftbd2iNmpqanDr1i1oaWkBAPT19aGpqSnWZklJCWJiYlrcZlM664dwzpw5mDhxYof3SwghhBDyT1VVVdWu7WdlZUFLSwuDBw+GpqYmunTpAhUVFQgEgnbtl3QuShzbkaenJ/bv349Dhw4hNTUVCxcuRHl5OebOnQsAcHR0xKpVq7j6GzduxO+//467d+8iPj4es2bNQm5uLubNmwegdgjb3d0dmzdvxunTp3Hr1i04OjpCW1ubki9CCCGEfDBEohrcu5OM1KjLuHcnGSJRTbv3WVpaCgcHB8jLy0NLSwsBAQHcB+8jR45Ebm4uPDw8uLO461y9ehXDhg2DrKwsdHR0sGTJEpSXl7eqby8vLxgZGUFOTg49e/aEt7c3qquruesbNmyAmZkZDhw4AH19fe4c8D///BNDhw6FjIwMTExMcOHChQZTIu/du4epU6dCWVkZKioqsLe3R05OTpOxzJkzB4sXL0ZeXh54PB6EQiGAhoMQQqEQvr6++L//+z8IBALo6upi3759Ym29Sd+XL1/Gzp07ufc5JycHwcHBUFZWFqsbFhYm9n2oe4+OHDkCoVAIJSUlTJ8+HaWlpVwdkUgEPz8/6OvrQ1ZWFv3798fPP/8s1u65c+dgZGQEWVlZfPLJJ83G+6pTp07B3NwcMjIy6NmzJ3x8fPDy5csW3fuu6NLZAfyTTZs2DY8ePcK6detQUFAAMzMzhIeHc5vb5OXlQULif7n7s2fP4OLigoKCAnTt2hUWFhaIjo6GiYkJV2fFihUoLy+Hq6srioqKMHToUISHh3O/ID5E1dXVkJSU7OwwCCGEENIBMmKi8UfwPpQ9fcyVKaio4tM5rjC0Gtxu/Xp6eiIqKgqnT5+GhoYG1q1bh/j4eJiZmeHEiRPo378/XF1d4eLiwt2TlZUFOzs7bN68GQcPHsSjR4/g5uYGNzc3BAUFtbhvgUCA4OBgaGtr49atW3BxcYFAIMCKFSu4OpmZmTh+/DhOnDgBPp+PmpoaTJw4Ebq6uoiJiUFpaSm++uorsXarq6tha2sLa2trREZGokuXLti8eTPs7OyQnJwMKSmpBrHs3LkTBgYG2LdvH+Li4sDn85uM29/fH5s2bcLq1avx888/Y+HChRgxYgR69+79xn2np6ejb9++2LhxIwBATU2txe9jVlYWwsLCcObMGTx79gxTp07F119/jS1btgCo3VDyhx9+QGBgIAwNDXHlyhXMmjULampqGDFiBO7du4dJkyZh0aJFcHV1xY0bNxq8p42JjIyEo6Mjdu3ahWHDhiErKwuurq4AgPXr17c4/k7HyAenuLiYAWDFxcVc2YgRI9iiRYvYokWLmKKiIuvWrRtbu3YtE4lEjDHGHjx4wD7//HMmIyPDhEIhO3r0KNPT02MBAQFcG8+ePWPOzs5MVVWVCQQC9sknn7DExMRmY3FycmL29vbc819//ZUNGTKEKSkpMRUVFTZ27FiWmZnJXc/OzmYAWEhICBs+fDiTlpZmQUFBrLq6mi1evJi7b8WKFczR0VGs7ZqaGubr68uEQiGTkZFhpqam7Keffnrt+xUZGcmGDh3KZGRkWI8ePdjixYtZWVkZd11PT49t2bKFzZ07lykoKDAdHR22d+/e17ZLCCGEfGhevHjBUlJS2IsXL97o/vTrUWzb1LFNPtKvR7VxxLVKSkqYpKSk2N8NRUVFTE5Oji1dupQxxhr8XcQYY87OzszV1VWsLDIykklISDT7HgBgJ0+ebPL6t99+yywsLLjn69evZ5KSkuzhw4dc2a+//sq6dOnC8vPzubLz58+LtX3kyBHWu3dv7u89xhirrKxksrKy7Lfffmuy/4CAAKanpydWNmLECO69YKz2/Zg1axb3XCQSMXV1dbZnz5636rt+P4wxFhQUxJSUlMTKTp48yV5NddavX8/k5ORYSUkJV7Z8+XJmZWXFGGOsoqKCycnJsejoaLF2nJ2d2YwZMxhjjK1atYqZmJiIXffy8mIA2LNnz5qMedSoUczX11es7MiRI0xLS4t7/rrv+dtq7mevsdygMTRVlXAOHTqELl26IDY2Fjt37sT27dtx4MABALXTah88eICIiAgcP34c+/btw8OHD8XunzJlCh4+fIhff/0VN2/ehLm5OUaNGoWnT5+2OIby8nJ4enrixo0buHjxIiQkJPDFF19AJBKJ1Vu5ciWWLl2K1NRU2NraYuvWrTh69CiCgoIQFRWFkpKSBjtT+fn54fDhwwgMDMSdO3fg4eGBWbNm4fLly03GU/dJ4eTJk5GcnIzQ0FBcvXoVbm5uYvX8/f0xcOBAJCQk4Msvv8TChQuRlpbW4tdNCCGEkOaJRDX4I3hfs3UuHdrXLtNW7969i+rqalhaWnJlSkpK6N27d7P3JSUlITg4GAoKCtzD1tYWIpEI2dnZ8PX1FbuWl5fXaDuhoaEYMmQINDU1oaCggLVr1zaoq6enJzb6lpaWBh0dHbENFF+Nvy6+zMxMCAQCLgYVFRVUVFQgKysLkZGRYvEdPXq0xe8ZAJiamnJf83g8aGpqcn8/tnffjREKhWLrMLW0tLh4MjMz8fz5c4wePVqs38OHDyMrKwsAkJqaCisrK7E26+8z8uq9CxYs4F7rxo0bxa65uLggPz8fz58/f+vX1VFoqirh6OjoICAgADweD71798atW7cQEBCAYcOG4cKFC4iLi8PAgQMBAAcOHIChoSF379WrVxEbG4uHDx9CWloaALBt2zaEhYXh559/5objX2fy5Mlizw8ePAg1NTWkpKSgb9++XLm7uzsmTZrEPd+9ezdWrVqFL774AgDw73//G+fOneOuV1ZWwtfXFxcuXOB+wHv27ImrV69i7969GDFiRKPx+Pn5wcHBgZuzb2hoiF27dmHEiBHYs2cPN0X4888/x5dffgmgdh1CQEAALl269Nr/oRBCCCGkZe6n3hGbntqY0iePcT/1DnQ+Mm22XkcpKyvD/PnzsWTJkgbXdHV1sWDBAkydOpUr09bWblDv2rVrcHBwgI+PD2xtbaGkpISQkBD4+/uL1ZOXl3+j+CwsLBpNytTU1CAlJYXExESurP5Z4q9TfykRj8fjBgPasm8JCQkwxsTKXl0D2tJ4AODs2bPo3r27WL26v21b4tWY604yKCsrg4+Pj9jfrnXep+VmlDgSzscffyy2iNja2hr+/v5IS0tDly5dYG5uzl3r1asXunbtyj1PSkpCWVkZunXrJtbmixcvkJWVhby8PLG1mqtXr8bq1asbxJCRkYF169YhJiYGjx8/5n6Y8/LyxBLHugQWAIqLi1FYWCj2KRqfz4eFhQV3/6ufIr2qqqoKAwYMAAB89NFHyM3NBQAMGzYMv/76K5KSkpCcnCz2S40xxn1SaGxsDKD5T9QIIYQQ8vbKip61ab3W6NmzJyQlJREXFwddXV0AtX9/pKenY/jw4QBqd9SvqREf7TQ3N0dKSgp69erVaLsqKipQUVFptu/o6Gjo6elhzZo1XFnd3yvN6d27N+7du4fCwkIu6YqLi2sQX2hoKNTV1Zs8rq2p2N/Wm/bd2PuspqaG0tJSlJeXcwn0qwlcS5iYmEBaWhp5eXlNDigYGxvj9OnTYmXXr19/bczm5uZIS0trt/eyo1DiSNpEWVkZtLS0EBER0eCasrIylJWVxX6Am/olOX78eOjp6WH//v3Q1taGSCRC3759G2wr3dpP1VryKdK5c+e4T6dkZWW5+5r7pLBOc59gEUIIIeTtKSh3fX2lVtRrDYFAACcnJyxfvhwqKipQV1fH+vXrISEhwX3oLhQKceXKFUyfPh3S0tJQVVWFl5cXPv74Y7i5uWHevHmQl5dHSkoKzp8/j3//+98t6tvQ0BB5eXkICQnBoEGDcPbsWZw8efK1940ePRoGBgZwcnLCN998g9LSUqxduxYAuJgdHBzw7bffwt7eHhs3bkSPHj2Qm5uLEydOYMWKFejRo8cbvmOv96Z9C4VCxMTEICcnh5veamVlBTk5OaxevRpLlixBTEwMgoODWxWPQCDAsmXL4OHhAZFIhKFDh6K4uBhRUVFQVFSEk5MTFixYAH9/fyxfvhzz5s3DzZs3W9TPunXrMG7cOOjq6uJf//oXJCQkkJSUhNu3b2Pz5s2tirMz0RpHwomJiRF7fv36dRgaGqJ37954+fIlEhISuGuZmZl49ux/n+iZm5ujoKAAXbp0Qa9evcQeqqqqDcobSxyfPHmCtLQ0rF27FqNGjYKxsbFYH01RUlKChoaG2KdoNTU1iI+P556/+ilS/fh0dHQA1K4NqCurSy5f/aSw/qOx3b4IIYQQ0j66G38EBRXVZusIuqni/9u796io6/QP4O8BAUEUipABHEG5KBhXDyQilwyblda0fmoXWkKR1hUV8biiXMQDrHbWwnQ5oZtcbE2pzSR1UQhbcQVXTMk1VgUEhQgTLRFEGGC+vz88zkrCyAjMDPF+ncM5zvf6DD7z1Wc+N2unKYNy/9TUVPj4+OC3v/0tgoKC4OvrCycnJ0VXw6SkJFy9ehV2dnaKsYaurq4oKipCRUUF/Pz84OHhgQ0bNvTYJbU3L7/8MqKjo7F8+XK4u7ujpKQECQkJjz1PV1cXubm5aGlpgZeXF5YsWaJotXwQs5GREU6cOIHx48fj1VdfhZOTE8LDw9HW1tZrK+BAedJ7r1mzBrq6unB2doa5uTlqa2vx9NNPY8+ePcjLy4OLiwv27duHjRs3qhxTcnIyEhISsHnzZjg5OeE3v/kN/vGPf2DChAkA7jca7N+/H7m5uXBzc8OOHTuwadOmx15XKpXi8OHDKCgogJeXF6ZNm4atW7fCxsZG5Rg1anDm7SFt1tusqsbGxkJ0dLRw6dIlYe/evcKoUaOEHTt2CIIgCEFBQYKnp6dw+vRp4dy5c8Lzzz8vGBoaCh988IEgCPdnypoxY4bg5uYm5OfnCzU1NUJxcbEQGxsrnDlzptdYHp5VtaurSzAzMxPeeustobKyUjh27Jjg5eXVbZapB7OqlpWVdbtOSkqKYGZmJuTm5gqXLl1SzA47b948xTFxcXGCmZmZkJ2dLVRVVQlnz54Vtm/fLmRnZ/ca3/nz5wVDQ0MhMjJSKCsrEyoqKoTc3FwhMjJSEARB6OzqFCzHWQoR8RFCaUOp0NnVKQiCILi5uQmJiYl9+vsgIiIaLobqrKo9aWlpEUxMTIRdu3ap7Z79dfLkSQFAtxnraXgYiFlV2VWVFEJDQ3Hv3j14e3tDV1cXUVFRikltPv74Y4SHh8Pf3x9isRibN29GeXm54hsrkUiEvLw8xMXFYdGiRWhsbIRYLIa/v3+fB1Lr6OggJycHK1euxLPPPotJkyZh+/btCAwMfOy5MTExuH79OkJDQ6Grq4t33nkHUqm029pCycnJMDc3x+bNm1FdXQ1TU1N4enr2ONbygQffFMbFxcHPzw+CIMDOzg6vvfYaCq8V4t3Sd3Hz3k0cunIIp/JPwcLIAuu81/Xp/RIREZFqHJ6bjpdXxz6yjuNos2fw/NuDu45jWVkZLl26BG9vbzQ1NSnWEZw7d+6g3bO/Dhw4AGNjYzg4OKCqqgpRUVHw9fWFnZ2dpkOjIUgkCL+Ygoh+9e7cuQMTExM0NTU9cTeE77//HhKJBIWFhXjhhRcGOML+k8vlcHJywsKFC5GcnDzg1y+8VojVx1dDQPePjwj3xwykBqYiyCZowO9LREQ0lLW1taGmpgYTJkzo12yScnnX/VlWb/8MY9OnYO00BTo6vS9EPxDKysqwZMkSXL58Gfr6+pg6dSpSU1Ph4uIyqPftj48//hgpKSmora3FM888g6CgILz//vuPTGZIv37KPnt9rQ1YOA5DT1I4fv3112hpaYGLiwsaGhqwdu1a1NfXo6Ki4pGJYTTh2rVrKCgoQEBAANrb25GWloasrCycP39eMfPpQOmSd0G6X4ofW3/scb8IIlgYWeDo/x2F7iD/I0ZERDSUDFThSESqGYjCkZPjUJ90dHQgNjYWU6ZMwSuvvAJzc3McP35cK4pG4H431+zsbHh5ecHX1xcXLlxAYWHhgBeNAHDuxrlei0YAECDgeut1nLtxrtdjiIiIiIiGEo5xpD6RSqWQSqWaDqNXEokExcXFarlXY2vjgB5HRERERKTt2OJIpCJzI/MBPY6IiGi44UgpIvUaiM8cC0ciFXmO9YSFkYViIpxfEkEEsZEYnmM91RwZERGRdnsw27lMJtNwJETDS2trKwD0a5gZu6oSqUhXRxfrvNdh9fHVEEHUbWbVB8VkjHcMJ8YhIiL6hREjRsDIyAiNjY3Q09ODjg7bMIgGkyAIaG1txY0bN2BqatptqTpVcVbVYWggluMgKNZxfHiiHLGRGDHeMVyKg4iIqBcymQw1NTWQy+WaDoVo2DA1NYVYLIZI9GiPOS7HQb1i4ThwuuRdOHfjHBpbG2FuZA7PsZ5saSQiInoMuVzO7qpEaqKnp6e0pbGvtQG7qhL1g66OLrzEXpoOg4iIaEjR0dHhOo5EQww7lhMREREREZFSLByJiIiIiIhIKRaOREREREREpBTHOA5DD+ZDunPnjoYjISIiIiIiTXpQEzxuzlQWjsNQc3MzAEAikWg4EiIiIiIi0gbNzc0wMTHpdT+X4xiG5HI5fvjhB4wePbrHtVxIu925cwcSiQR1dXVcToVUxvyh/mIOUX8wf6g/mD+DQxAENDc3w8rKCjo6vY9kZIvjMKSjo4Nx48ZpOgzqpzFjxvChSU+M+UP9xRyi/mD+UH8wfwaespbGBzg5DhERERERESnFwpGIiIiIiIiUYuFINMQYGBggMTERBgYGmg6FhiDmD/UXc4j6g/lD/cH80SxOjkNERERERERKscWRiIiIiIiIlGLhSEREREREREqxcCQiIiIiIiKlWDgSERERERGRUiwcibRIeno6XF1dFQvb+vj44MiRI70en52dDZFI1O1n5MiRaoyYtI2qOQQAt2/fRmRkJCwtLWFgYABHR0fk5eWpKWLSJqrmT2Bg4CPPIJFIhJdeekmNUZO2eJLnzwcffIBJkybB0NAQEokE0dHRaGtrU1PEpE1UzZ+Ojg4kJSXBzs4OI0eOhJubG44eParGiIefEZoOgIj+Z9y4cXj33Xfh4OAAQRCwe/duzJ07F2VlZZgyZUqP54wZMwaXL19WvBaJROoKl7SQqjkkk8kwa9YsjB07Fp9//jmsra1x7do1mJqaqj940jhV8+eLL76ATCZTvL516xbc3NywYMECdYZNWkLV/Nm7dy/WrVuHzMxMTJ8+HRUVFQgLC4NIJEJqaqoG3gFpkqr5Ex8fjz179uCjjz7C5MmTkZ+fj1deeQUlJSXw8PDQwDsYBgQi0mpPPfWUsGvXrh73ZWVlCSYmJuoNiIYcZTmUnp4uTJw4UZDJZGqOioYKZfnzS1u3bhVGjx4ttLS0DHJUNFQoy5/IyEhh5syZ3batXr1a8PX1VUdoNAQoyx9LS0shLS2t27ZXX31VCAkJUUdowxK7qhJpqa6uLuTk5ODu3bvw8fHp9biWlhbY2NhAIpFg7ty5KC8vV2OUpM36kkMHDx6Ej48PIiMjYWFhgWeffRabNm1CV1eXmqMlbdPXZ9DDMjIy8Prrr2PUqFGDHB1pu77kz/Tp03H27FmUlpYCAKqrq5GXl4fg4GB1hkpaqC/5097e/sjwHENDQ5w8eVIdIQ5L7KpKpGUuXLgAHx8ftLW1wdjYGAcOHICzs3OPx06aNAmZmZlwdXVFU1MT3nvvPUyfPh3l5eUYN26cmiMnbaFKDlVXV+Prr79GSEgI8vLyUFVVhWXLlqGjowOJiYlqjpy0gSr587DS0lJ89913yMjIUEOUpK1UyZ8333wTN2/exIwZMyAIAjo7O7F06VLExsaqOWrSFqrkj1QqRWpqKvz9/WFnZ4djx47hiy++4Befg0gkCIKg6SCI6H9kMhlqa2vR1NSEzz//HLt27UJRUVGf/uPW0dEBJycnvPHGG0hOTlZDtKSNVMkhR0dHtLW1oaamBrq6ugCA1NRUbNmyBQ0NDeoOnbTAkz6Dfv/73+PUqVP4z3/+o6ZISRupkj/Hjx/H66+/jpSUFDz33HOoqqpCVFQUIiIikJCQoIHoSdNUyZ/GxkZERETg0KFDEIlEsLOzQ1BQEDIzM3Hv3j0NRP/rx8KRSMsFBQXBzs4OO3fu7NPxCxYswIgRI7Bv375BjoyGCmU5FBAQAD09PRQWFiq2HTlyBMHBwWhvb4e+vr46QyUt1Jdn0N27d2FlZYWkpCRERUWpMTrSdsryx8/PD9OmTcOWLVsU2/bs2YN33nkHLS0t0NHhiKrhri/Pn7a2Nty6dQtWVlZYt24dDh8+zGE7g4SfSCItJ5fL0d7e3qdju7q6cOHCBVhaWg5yVDSUKMshX19fVFVVQS6XK7ZVVFTA0tKSRSMB6Nsz6O9//zva29vx1ltvqSkqGiqU5U9ra+sjxeGDng9s1yCgb8+fkSNHwtraGp2dndi/fz/mzp2rpuiGH45xJNIi69evx+zZszF+/Hg0Nzdj7969OH78OPLz8wEAoaGhsLa2xubNmwEASUlJmDZtGuzt7XH79m1s2bIF165dw5IlSzT5NkiDVM2hP/zhD0hLS0NUVBRWrFiByspKbNq0CStXrtTk2yANUTV/HsjIyMC8efNgZmamibBJS6iaP3PmzEFqaio8PDwUXVUTEhIwZ84cRQFJw4eq+XP69GnU19fD3d0d9fX12LhxI+RyOdauXavJt/GrxsKRSIvcuHEDoaGhaGhogImJCVxdXZGfn49Zs2YBAGpra7t9O/vzzz8jIiIC169fx1NPPYWpU6eipKSkT+Mh6ddJ1RySSCTIz89HdHQ0XF1dYW1tjaioKMTExGjqLZAGqZo/AHD58mWcPHkSBQUFmgiZtIiq+RMfHw+RSIT4+HjU19fD3Nwcc+bMwZ/+9CdNvQXSIFXzp62tDfHx8aiuroaxsTGCg4Pxt7/9jesQDyKOcSQiIiIiIiKlOMaRiIiIiIiIlGLhSEREREREREqxcCQiIiIiIiKlWDgSERERERGRUiwciYiIiIiISCkWjkRERERERKQUC0ciIiIiIiJSioUjERHRELRx40a4u7srXoeFhWHevHkai4eIiH7dWDgSERENkLq6OixevBhWVlbQ19eHjY0NoqKicOvWrUG/97Zt25Cdna14HRgYiFWrVvX7uq2trVi/fj3s7OwwcuRImJubIyAgAF9++WW/r01EREPHCE0HQERE9GtQXV0NHx8fODo6Yt++fZgwYQLKy8vxxz/+EUeOHMG///1vPP3004N2fxMTk0G57tKlS3H69Gn85S9/gbOzM27duoWSkpJBLYZlMhn09fUH7fpERKQ6tjgSERENgMjISOjr66OgoAABAQEYP348Zs+ejcLCQtTX1yMuLk5xrEgkQm5ubrfzTU1Nu7UYxsTEwNHREUZGRpg4cSISEhLQ0dHR6/0f7qoaFhaGoqIibNu2DSKRCCKRCDU1NbC3t8d7773X7bxvv/0WIpEIVVVVPV734MGDiI2NRXBwMGxtbTF16lSsWLECixcvVhzT3t6OmJgYSCQSGBgYwN7eHhkZGYr9RUVF8Pb2hoGBASwtLbFu3Tp0dnYq9gcGBmL58uVYtWoVnnnmGUilUgDAd999h9mzZ8PY2BgWFhb43e9+h5s3b/b6OyAiosHDwpGIiKiffvrpJ+Tn52PZsmUwNDTstk8sFiMkJASffvopBEHo8zVHjx6N7Oxs/Pe//8W2bdvw0UcfYevWrX06d9u2bfDx8UFERAQaGhrQ0NCA8ePHY/HixcjKyup2bFZWFvz9/WFvb9/jtcRiMfLy8tDc3Nzr/UJDQ7Fv3z5s374dFy9exM6dO2FsbAwAqK+vR3BwMLy8vHD+/Hmkp6cjIyMDKSkp3a6xe/du6Ovro7i4GDt27MDt27cxc+ZMeHh44JtvvsHRo0fx448/YuHChX36HRAR0cBiV1UiIqJ+qqyshCAIcHJy6nG/k5MTfv75ZzQ2NmLs2LF9umZ8fLziz7a2tlizZg1ycnKwdu3ax55rYmICfX19GBkZQSwWK7aHhYVhw4YNKC0thbe3Nzo6OrB3795HWiEf9te//hUhISEwMzODm5sbZsyYgfnz58PX1xcAUFFRgc8++wxfffUVgoKCAAATJ05UnP/hhx9CIpEgLS0NIpEIkydPxg8//ICYmBhs2LABOjr3v8N2cHDAn//8Z8V5KSkp8PDwwKZNmxTbMjMzIZFIUFFRAUdHx8f+HoiIaOCwxZGIiGiAPK5FUZVxe59++il8fX0hFothbGyM+Ph41NbW9is+KysrvPTSS8jMzAQAHDp0CO3t7ViwYEGv5/j7+6O6uhrHjh3D/PnzUV5eDj8/PyQnJwO439VVV1cXAQEBPZ5/8eJF+Pj4QCQSKbb5+vqipaUF33//vWLb1KlTu513/vx5/POf/4SxsbHiZ/LkyQCAK1euPNkvgIiInhgLRyIion6yt7eHSCTCxYsXe9x/8eJFmJubw9TUFMD9MY6/LDIfHr946tQphISEIDg4GIcPH0ZZWRni4uIgk8n6HeuSJUuQk5ODe/fuISsrC6+99hqMjIyUnqOnpwc/Pz/ExMSgoKAASUlJSE5Ohkwme6Rr7pMaNWpUt9ctLS2YM2cOvv32224/lZWV8Pf3H5B7EhFR37GrKhERUT+ZmZlh1qxZ+PDDDxEdHd2tmLp+/To++eQTREZGKraZm5ujoaFB8bqyshKtra2K1yUlJbCxsek2oc61a9dUiklfXx9dXV2PbA8ODsaoUaOQnp6Oo0eP4sSJEypdFwCcnZ3R2dmJtrY2uLi4QC6Xo6ioSNFV9WFOTk7Yv38/BEFQtDoWFxdj9OjRGDduXK/38PT0xP79+2Fra4sRI/jfFSIiTWOLIxER0QBIS0tDe3s7pFIpTpw4gbq6Ohw9ehSzZs2Co6MjNmzYoDh25syZSEtLQ1lZGb755hssXboUenp6iv0ODg6ora1FTk4Orly5gu3bt+PAgQMqxWNra4vTp0/j6tWruHnzJuRyOQBAV1cXYWFhWL9+PRwcHODj46P0OoGBgdi5cyfOnj2Lq1evIi8vD7GxsXj++ecxZswY2Nra4u2338bixYuRm5uLmpoaHD9+HJ999hkAYNmyZairq8OKFStw6dIlfPnll0hMTMTq1asV4xt7EhkZiZ9++glvvPEGzpw5gytXriA/Px+LFi3qsSAmIqLBxcKRiIhoADg4OODMmTOYOHEiFi5cCBsbG8yePRuOjo4oLi5WzDIKAO+//z4kEgn8/Pzw5ptvYs2aNd26i7788suIjo7G8uXL4e7ujpKSEiQkJKgUz5o1a6CrqwtnZ2eYm5t3Gx8ZHh4OmUyGRYsWPfY6UqkUu3fvxosvvggnJyesWLECUqlUURgCQHp6OubPn49ly5Zh8uTJiIiIwN27dwEA1tbWyMvLQ2lpKdzc3LB06VKEh4d3m/ynJ1ZWViguLkZXVxdefPFFuLi4YNWqVTA1NVVacBIR0eAQCarMDU5ERER9lpiYiNTUVHz11VeYNm2apsNR+Ne//oUXXngBdXV1sLCw0HQ4REQ0BLBwJCIiGkRZWVloamrCypUrNd5S1t7ejsbGRrz99tsQi8X45JNPNBoPERENHSwciYiIhons7GyEh4fD3d0dBw8ehLW1taZDIiKiIYKFIxERERERESnF0eVERERERESkFAtHIiIiIiIiUoqFIxERERERESnFwpGIiIiIiIiUYuFIRERERERESrFwJCIiIiIiIqVYOBIREREREZFSLByJiIiIiIhIKRaOREREREREpNT/A5FNTfySKzJ+AAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "044e2e42-f6eb-494e-89db-a617bc72da8b",
   "metadata": {},
   "source": [
    "Much better validation scores but it's not worth the effort compared to using our base gte-large embedding model. This again can be improved with larger/higher quality datasets and perhaps even a larger testing dataset to capture small improvements in our retrieval scores. If we did want to use this fine-tuned embedding model, we can set these values:\n",
    "\n",
    "```python\n",
    "experiment_name = \"gte-large-fine-tuned-el\"\n",
    "EMBEDDING_MODEL_PATH = str(Path(EFS_DIR, experiment_name))  # can pass this in directly for embedding_model_name\n",
    "SQL_DUMP_FP = Path(EFS_DIR, \"sql_dumps\", f\"{experiment_name}_{CHUNK_SIZE}_{CHUNK_OVERLAP}.sql\")\n",
    "run_experiment(embedding_model_name=EMBEDDING_MODEL_PATH, sql_dump_fp=SQL_DUMP_FP, ...)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5e41783-6f45-4bdc-8a1b-fe3329f7b101",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Prompt engineering"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbb495d1-557e-41eb-8c27-fe8cea02e7b5",
   "metadata": {
    "tags": []
   },
   "source": [
    "There's too much we can do when it comes to engineering the prompt (x-of-thought, multimodal, self-refine, query decomposition, etc.) so we're going to try out just a few interesting ideas. We're going to allow the LLM to ignore anything not relevant. The idea here is to show how quickly we can go from prompt engineering to evaluation report.\n",
    "\n",
    "<img width=\"800\" src=\"https://images.ctfassets.net/xjan103pcp94/6nMOu5sm3jploFUXeKxog2/1e4405924686798b243eb80ff5b8d549/Screenshot_2023-11-01_at_2.40.56_PM.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1de8e133-52fc-42d8-ac3d-51e8557ed563",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "system_content = \"Answer the query using the context provided. Be succinct. \" \\\n",
    "    \"Contexts are organized in a list of dictionaries [{'text': <context>}, {'text': <context>}, ...]. \" \\\n",
    "    \"Feel free to ignore any contexts in the list that don't seem relevant to the query. \""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f618682c-33fb-45d2-8247-883e3136f829",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica, you can use the `ray.kill()` function with the replica handle. First, get the replica handle by calling `ray.get_actor()` and providing the NAME of the replica from the output of `ray list actors`. Here's an example:\n\n```python\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\nray.kill(replica_handle, no_restart=True)\n```\n\nThis will kill the specified replica without restarting it.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer provides a detailed and technically accurate method to kill a specific serve replica using the `ray.kill()` function. However, it does not perfectly match the reference answer, which states that there is no information available on how to perform the task. Despite this, the generated answer is still useful and informative.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 4,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-deployments",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#worker-node-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-applications",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#replica-health-checking",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#head-node-failure"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [19:56<00:00,  6.76s/it]\n"
     ]
    }
   ],
   "source": [
    "# Evaluate\n",
    "experiment_name = \"prompt-ignore-contexts\"\n",
    "run_experiment(\n",
    "    experiment_name=experiment_name, \n",
    "    chunk_size=CHUNK_SIZE, \n",
    "    chunk_overlap=CHUNK_OVERLAP, \n",
    "    num_chunks=NUM_CHUNKS,\n",
    "    embedding_model_name=EMBEDDING_MODEL_NAME,\n",
    "    embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "    llm=LLM,\n",
    "    evaluator=EVALUATOR,\n",
    "    docs_dir=DOCS_DIR, \n",
    "    experiments_dir=EXPERIMENTS_DIR, \n",
    "    references_fp=REFERENCES_FILE_PATH,\n",
    "    system_content=system_content,  # new prompt\n",
    "    num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8913f69e-25d1-4354-aaa0-13fb46778c83",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prompt-ignore-contexts\n",
      "  retrieval score: 0.7796610169491526\n",
      "  quality score: 3.8954802259887007\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'retrieval_score': 0.7796610169491526, 'quality_score': 3.8954802259887007}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Results\n",
    "print_experiment(experiment_name, EXPERIMENTS_DIR)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2674a08a-1447-42e6-84bf-72777a660392",
   "metadata": {},
   "source": [
    "It seems this specific prompt engineering effort does improve the quality of our system (knowing which context is relevant requires domain knowledge of Ray which the model may not have developed). But, as we mentioned earlier, there are too many other ways we can engineer our prompt and we encourage you to explore more. What’s important here is that we have a **clean and simple way to evaluate anything** that we want to experiment with. However, we have empirically found that improving the quality of our retrieval system and the data flywheel (where we fix our documentation itself) has had a much larger impact on the overall quality of our system."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9cd22d32-3eba-4a1e-a79c-e5f85cdd5114",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "SYSTEM_CONTENT = \"Answer the query using the context provided. Be succinct.\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f3fcd85-01ac-4cd4-8dc1-cf864bb646ef",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Lexical search"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63edccae-c7b5-4452-b97c-5d3e53b6a1cb",
   "metadata": {},
   "source": [
    "We're going to now supplement our vector embedding based search with traditional lexical search, which searches for exact token matches between our query and document chunks. Our intuition here is that lexical search can help identify chunks with exact keyword matches where semantic representation may fail to capture. Especially for tokens that are out-of-vocabulary (and so represented via subtokens) with our embedding model. But our embeddings based approach is still very advantageous for capturing implicit meaning, and so we're going to combine several retrieval chunks from both vector embeddings based search and lexical search.\n",
    "\n",
    "<img width=\"800\" src=\"https://images.ctfassets.net/xjan103pcp94/9eBIE4iw7SmTtVvANbkAq/8913fcbd10fc66fd8b59278642155609/rag-based-llm-applications-lexical-search.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b29782e2-786a-4665-aa13-d9339ef3dad4",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Env vars\n",
    "os.environ[\"EMBEDDING_DIM\"] = f\"{EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME]}\"\n",
    "os.environ[\"SQL_DUMP_FP\"] = str(Path(EFS_DIR, \"sql_dumps\", f\"{EMBEDDING_MODEL_NAME.split('/')[-1]}_{CHUNK_SIZE}_{CHUNK_OVERLAP}.sql\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b85823f-f6fa-480a-8b24-0da188a55a2f",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "DROP TABLE\n",
      "CREATE TABLE\n",
      "SET\n",
      "SET\n",
      "SET\n",
      "SET\n",
      "SET\n",
      " set_config \n",
      "------------\n",
      " \n",
      "(1 row)\n",
      "\n",
      "SET\n",
      "SET\n",
      "SET\n",
      "SET\n",
      "ALTER TABLE\n",
      "ALTER TABLE\n",
      "ALTER TABLE\n",
      "ALTER TABLE\n",
      "DROP SEQUENCE\n",
      "DROP TABLE\n",
      "DROP SEQUENCE\n",
      "DROP TABLE\n",
      "DROP EXTENSION\n",
      "CREATE EXTENSION\n",
      "COMMENT\n",
      "SET\n",
      "SET\n",
      "CREATE TABLE\n",
      "ALTER TABLE\n",
      "CREATE SEQUENCE\n",
      "ALTER SEQUENCE\n",
      "ALTER SEQUENCE\n",
      "CREATE TABLE\n",
      "ALTER TABLE\n",
      "CREATE SEQUENCE\n",
      "ALTER SEQUENCE\n",
      "ALTER SEQUENCE\n",
      "ALTER TABLE\n",
      "ALTER TABLE\n",
      "COPY 40433\n",
      "COPY 14774\n",
      " setval \n",
      "--------\n",
      "  40433\n",
      "(1 row)\n",
      "\n",
      " setval \n",
      "--------\n",
      "  14774\n",
      "(1 row)\n",
      "\n",
      "ALTER TABLE\n",
      "ALTER TABLE\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "# Ensure the right index in built in-memory\n",
    "psql \"$DB_CONNECTION_STRING\" -c \"DROP TABLE IF EXISTS document;\"  # drop\n",
    "sudo -u postgres psql -f ../migrations/vector-${EMBEDDING_DIM}.sql  # set up\n",
    "psql \"$DB_CONNECTION_STRING\" -f $SQL_DUMP_FP  # load"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c07fb08c-b395-4a77-9968-581d63aacca0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Get chunks\n",
    "with psycopg.connect(os.environ[\"DB_CONNECTION_STRING\"]) as conn:\n",
    "    register_vector(conn)\n",
    "    with conn.cursor() as cur:\n",
    "        cur.execute(\"SELECT id, text, source FROM document\")\n",
    "        chunks = cur.fetchall()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19e62598-3116-466e-8a60-fc571ce698df",
   "metadata": {},
   "source": [
    "## BM25"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65023148-7fd1-4383-b28c-b417103e716d",
   "metadata": {},
   "source": [
    "Let's apply lexical search using [BM25](https://en.wikipedia.org/wiki/Okapi_BM25), which is a ranking algorithm that rewards unique token matches between our query and contexts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "206e3523-f651-425d-817a-ee359700cac9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import re\n",
    "from rank_bm25 import BM25Okapi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f05f3014-710c-4edd-b6c9-d94299a7d33e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# BM25 index\n",
    "texts = [re.sub(r\"[^a-zA-Z0-9]\", \" \", chunk[1]).lower().split() for chunk in chunks]\n",
    "lexical_index = BM25Okapi(texts)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc5470e0-8430-4cf2-8b59-7f6c2674c25b",
   "metadata": {},
   "source": [
    "Similar to our `semantic_search` function to retrieve the relevant context, we can implement a search function to use our lexical index to retrieve relevant context."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "698222a6-2b4e-4b8d-bb64-551ae09595bb",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def lexical_search(index, query, chunks, k):\n",
    "    query_tokens = query.lower().split()  # preprocess query\n",
    "    scores = index.get_scores(query_tokens)  # get best matching (BM) scores\n",
    "    indices = sorted(range(len(scores)), key=lambda i: -scores[i])[:k]  # sort and get top k\n",
    "    lexical_context = [{\n",
    "            \"id\": chunks[i][0], \n",
    "            \"text\": chunks[i][1], \n",
    "            \"source\": chunks[i][2], \n",
    "            \"score\": scores[i]} for i in indices]\n",
    "    return lexical_context"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca7734e4-ea44-483c-93ed-e7e6f646caf8",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-size\n",
      "Configuring batch size#\n",
      "Increasing batch_size improves the performance of vectorized transformations like\n",
      "NumPy functions and model inference. However, if your batch size is too large, your\n",
      "program might run out of memory. If you encounter an out-of-memory error, decrease your\n",
      "batch_size.\n",
      "Note\n",
      "The default batch size depends on your resource type. If you’re using CPUs,\n",
      "the default batch size is 4096. If you’re using GPUs, you must specify an explicit\n",
      "batch size.\n",
      "\n",
      "https://docs.ray.io/en/master/data/api/doc/ray.data.Dataset.map_batches.html#ray-data-dataset-map-batches\n",
      "fn – The function or generator to apply to a record batch, or a class type\n",
      "that can be instantiated to create such a callable. Callable classes are\n",
      "only supported for the actor compute strategy. Note fn must be\n",
      "pickle-able.\n",
      "batch_size – The desired number of rows in each batch, or None to use\n",
      "entire blocks as batches (blocks may contain different numbers of rows).\n",
      "The actual size of the batch provided to fn may be smaller than\n",
      "batch_size if batch_size doesn’t evenly divide the block(s) sent\n",
      "to a given map task. Default batch_size is 4096 with “default”.\n",
      "compute – Either “tasks” (default) to use Ray Tasks or an\n",
      "ActorPoolStrategy to use an autoscaling actor pool.\n",
      "\n",
      "https://docs.ray.io/en/master/data/api/doc/ray.data.Dataset.take_batch.html#ray-data-dataset-take-batch\n",
      "ray.data.Dataset.take_batch#\n",
      "Dataset.take_batch(batch_size: int = 20, *, batch_format: Optional[str] = 'default') → Union[pyarrow.Table, pandas.DataFrame, Dict[str, numpy.ndarray]][source]#\n",
      "Return up to batch_size rows from the Dataset in a batch.\n",
      "Ray Data represents batches as NumPy arrays or pandas DataFrames. You can\n",
      "configure the batch type by specifying batch_format.\n",
      "This method is useful for inspecting inputs to map_batches().\n",
      "\n",
      "Warning\n",
      "take_batch() moves up to batch_size rows to the caller’s\n",
      "machine. If batch_size is large, this method can cause an `\n",
      "OutOfMemory error on the caller.\n",
      "\n",
      "\n",
      "Note\n",
      "This operation will trigger execution of the lazy transformations performed on this dataset.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Retrieve top-k docs\n",
    "k = 3\n",
    "query = \"What is the default batch size for map_batches?\"\n",
    "top_docs = lexical_search(lexical_index, query, chunks, k=k)\n",
    "for item in top_docs:\n",
    "    print (item[\"source\"])\n",
    "    print (item[\"text\"])\n",
    "    print ()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c884bbc-76e5-40ed-b7d5-2a5733fc9c86",
   "metadata": {},
   "source": [
    "## Semantic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9785a5f-7e62-4727-9dda-d038d640f105",
   "metadata": {},
   "source": [
    "Comparing this with the retrieved sources with our existing vector embedding based search shows that the two approaches, while different, both retrieved relevant sources. So, we're going to combine both approaches and feed it into the context for our LLM for generation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6711aa7b-7e2a-43d2-9043-e03dc7a32ba0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1024"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Embed query\n",
    "embedding_model = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME)\n",
    "embedding = np.array(embedding_model.embed_query(query))\n",
    "len(embedding)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7afafafe-5df4-4087-8f28-bd79d6d931e2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Get context\n",
    "with psycopg.connect(os.environ[\"DB_CONNECTION_STRING\"]) as conn:\n",
    "    register_vector(conn)\n",
    "    with conn.cursor() as cur:\n",
    "        cur.execute(\"SELECT * FROM document ORDER BY embedding <=> %s LIMIT %s\", (embedding, k))\n",
    "        rows = cur.fetchall()\n",
    "        context = [{\"text\": row[1]} for row in rows]\n",
    "        sources = [row[2] for row in rows]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1698b140-f3a3-4026-ad4a-c378192dfc47",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://docs.ray.io/en/master/data/batch_inference.html#configuring-batch-size\n",
      "# Specify that each input batch should be of size 2.\n",
      "ds.map_batches(assert_batch, batch_size=2)\n",
      "Caution\n",
      "The default batch_size of 4096 may be too large for datasets with large rows\n",
      "(for example, tables with many columns or a collection of large images).\n",
      "\n",
      "https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-size\n",
      "Configuring batch size#\n",
      "Increasing batch_size improves the performance of vectorized transformations like\n",
      "NumPy functions and model inference. However, if your batch size is too large, your\n",
      "program might run out of memory. If you encounter an out-of-memory error, decrease your\n",
      "batch_size.\n",
      "Note\n",
      "The default batch size depends on your resource type. If you’re using CPUs,\n",
      "the default batch size is 4096. If you’re using GPUs, you must specify an explicit\n",
      "batch size.\n",
      "\n",
      "https://docs.ray.io/en/master/data/examples/pytorch_resnet_batch_prediction.html#model-inference\n",
      "}\n",
      "Then we use the map_batches() API to apply the model to the whole dataset.\n",
      "The first parameter of map_batches is the user-defined function (UDF), which can either be a function or a class. Since we are using a class in this case, the UDF will run as long-running Ray actors. For class-based UDFs, we use the compute argument to specify ActorPoolStrategy with the number of parallel actors.\n",
      "The batch_size argument indicates the number of images in each batch. See the Ray dashboard\n",
      "for GPU memory usage to experiment with the batch_size when using your own model and dataset.\n",
      "You should aim to max out the batch size without running out of GPU memory.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for i, item in enumerate(context):\n",
    "    print (sources[i])\n",
    "    print (item[\"text\"])\n",
    "    print ()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d100290c-fdaa-4d57-877f-01f621698433",
   "metadata": {},
   "source": [
    "## Experiment"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c76aebae-f7ae-469c-bfcf-319b588d78ae",
   "metadata": {},
   "source": [
    "Now let's incorporate this into our retrieval workflow by add it to our `generate.py/QueryAgent` class. The main change will be to include the additional sources from lexical search:\n",
    "```python\n",
    "def QueryAgent():\n",
    "    def __call__(lexical_search_k=1, **kwargs):\n",
    "        # Add lexical search results\n",
    "        if self.lexical_index:\n",
    "            lexical_context = lexical_search(\n",
    "                index=self.lexical_index, query=query, chunks=self.chunks, k=lexical_search_k)\n",
    "            # Insert after <lexical_search_k> worth of semantic results\n",
    "            context_results[lexical_search_k:lexical_search_k] = lexical_context\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95e2cdf8-4c53-414c-b302-990fc79b25e1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "lexical_search_k_list = [1, 3, 5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f8ef2b7f-abf5-4604-9bf3-faa1d6e3d8d8",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica, you can use the following steps:\n\n1. Get the actor handle for the replica using `ray.get_actor()` with the NAME of the replica from the output of `ray list actors`.\n2. Use `ray.kill()` to kill the replica, passing the replica handle as the first argument and setting `no_restart=True`.\n\nHere is an example:\n\n```python\nimport ray\n\n# Get the actor handle for the replica\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\n\n# Kill the replica\nray.kill(replica_handle, no_restart=True)\n```\n\nThis will kill the replica and prevent it from being restarted. Other replicas will continue processing requests, and the killed replica will eventually be restarted and resume serving requests.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer is detailed, accurate, and provides a step-by-step guide on how to kill a specific serve replica. It even includes a Python code example for better understanding. Despite the reference answer stating that there's no information available, the generated answer provides a comprehensive response to the query.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 5,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-deployments",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/tune/faq.html#ray-tune-faq",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#replica-health-checking",
        "https://docs.ray.io/en/master/tune/faq.html#ray-tune-faq",
        "https://docs.ray.io/en/master/serve/api/doc/ray.serve.get_replica_context.html#ray-serve-get-replica-context",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#worker-node-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-applications",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#replica-health-checking",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#head-node-failure"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [19:50<00:00,  6.72s/it]\n"
     ]
    }
   ],
   "source": [
    "# Experiment\n",
    "use_lexical_search = True\n",
    "for lexical_search_k in lexical_search_k_list:\n",
    "    experiment_name = f\"lexical-search-bm25-{lexical_search_k}\"\n",
    "    experiment_names.append(experiment_name)\n",
    "    run_experiment(\n",
    "        experiment_name=experiment_name, \n",
    "        chunk_size=CHUNK_SIZE, \n",
    "        chunk_overlap=CHUNK_OVERLAP, \n",
    "        num_chunks=NUM_CHUNKS,\n",
    "        embedding_model_name=EMBEDDING_MODEL_NAME,\n",
    "        embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "        llm=LLM,\n",
    "        evaluator=EVALUATOR,\n",
    "        docs_dir=DOCS_DIR, \n",
    "        experiments_dir=EXPERIMENTS_DIR, \n",
    "        references_fp=REFERENCES_FILE_PATH,\n",
    "        system_content=SYSTEM_CONTENT,\n",
    "        use_lexical_search=use_lexical_search,\n",
    "        lexical_search_k=lexical_search_k,\n",
    "        num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aecb240f-dc95-48ab-bec9-cfea285467c2",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lexical-search-bm25-1\n",
      "  retrieval score: 0.7853107344632768\n",
      "  quality score: 4.019774011299435\n",
      "\n",
      "lexical-search-bm25-3\n",
      "  retrieval score: 0.7966101694915254\n",
      "  quality score: 3.9322033898305087\n",
      "\n",
      "lexical-search-bm25-5\n",
      "  retrieval score: 0.8022598870056498\n",
      "  quality score: 3.8954802259887007\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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6NGjmDNnDnr06AEhBFq0aIERI0a8cAxz585FvXr1MG/ePGRkZMDS0lLl3bjn1bJlS3zwwQfo378/7ty5g/feew9r1qx5oTajoqJw9epVXL16FdbW1irHhBDS5wULFuDGjRuoV68e2rRpg61bt+LDDz+ssO2bN29i5MiRuH37NszMzPD2228jLi4OZmZmLxQzEREREdHrTCae/kudiIiIiIiINMpzr1NHREREREREdY9JHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGqxeXQdA/6NUKpGRkQEjIyPIZLK6DoeIiIiIiOqIEAL379+HlZUVtLQqHotjUvcSycjIgI2NTV2HQUREREREL4n//ve/sLa2rrAOk7qXiJGREYCSG2dsbFzH0RARERERUV3Jz8+HjY2NlCNUhEndS6T0kUtjY2MmdUREREREVKnXsjRyopTVq1ejadOm0NPTg4uLC+Lj4yusv2LFCrRu3Rr6+vqwsbHB9OnT8fjx40q3eefOHXz22WdSG7a2tpg6dSry8vJU2pDJZGW2iIiI6us4ERERERHRP2hcUrd161b4+/sjKCgIf/31Fzp06AAPDw/k5OSorb9lyxbMnj0bQUFB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586dol69eqK4uFj8/vvvAkCZpLFevXpi+PDhQoiSpE5HR0clMSyN7Z9J3aeffqpSx9bWVixZskTaVyqVAoA0M/OHH34otLW1y1wfgNi3b1+57Q4aNEilX+qSuqCgIFGvXj3Rp08fsXjxYnH16tVyv+dXUXUldZwohYiIiIjoBRkaGpZZz/hpJ06cAADcuXMHd+7cUZkY7FkqO8mavr6+NGFYRZ41Edg/Jx178OABBg4cKL3a8bTSd8LLa/efk4n9U0UTsVHlMakjIiIiIqpBqampmD59OjZs2ICtW7fCx8cHBw8eVJlM6+TJkyrnxMXFoVWrVpDL5SqTrPXo0aO2w0fnzp3x73//G02bNkW9es+fPmhra0OhUJQpt7e3lyZjGzlyJMLCwpjUVREnSiEiIiIiekGFhYXS5Fil261bt6BQKDB69Gh4eHhg7NixCAsLw7lz57Bs2TKV89PS0uDv74/Lly/jt99+w8qVKzFt2jQAlZtkrSZNmTIFd+7cwciRI3Hq1CmkpqbiP//5D8aOHas2SStP06ZNER0djaysLNy9e/eZE7FR5XGkjoiIiIjoBR04cEDlUUQAaN26NUaNGoUbN27g999/B1DyuOL69esxcuRI9O3bFx06dAAAeHt749GjR3B2doZcLse0adMwceJEqa2wsDB8++23mDFjBtLT02Fqaopu3brhvffeq/G+WVlZ4c8//0RAQAD69u2LwsJC2NnZwdPTs1JL95RatmwZ/P39sWHDBjRp0gRXrlzB7du34e3tjezsbJiamuKDDz7AN998U4O9eTXJhHhqAQyqU/n5+TAxMUFeXl6VF0kmIiIiehk8fvwY165dQ7NmzaCnp1fX4WgENzc3dOzYEStWrKjrUKiWVfTzUpXcgI9fEhERERERaTAmdURERERERBqM79QREREREdWhI0eO1HUIpOE4UkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMI1M6lavXo2mTZtCT08PLi4uiI+Pr7D+ihUr0Lp1a+jr68PGxgbTp0/H48ePq9Tm48ePMWXKFDRq1Aj169fH0KFDkZ2drVInLS0NAwYMgIGBARo3boxZs2ahuLi4ejpNRERERDXKzc0Nn3/+ebW0FR4ejgYNGlRLW0DJsgcymQz37t2rtjZr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XeDs7IwVK1agoKAAY8eOBQB4e3ujSZMmWLx4scp5ISEhGDx4MBo1alSmzcjISJiZmcHW1hbnz5/HtGnTMHjwYPTt2xcAkJqaii1btqB///5o1KgRzp07h+nTp6Nnz55wdHQEAPTt2xft2rXDxx9/jCVLliArKwtfffUVpkyZwtE4IiIiIiKqMRqX1I0YMQK5ubmYN28esrKy0LFjRxw4cECalCQtLQ1aWqoDkJcvX0ZMTAz++OMPtW1mZmbC398f2dnZsLS0hLe3N+bOnSsd19HRwcGDB6UE0sbGBkOHDsVXX30l1ZHL5fj999/xySefwNXVFYaGhvDx8VH7uCcREREREVF1qdF16qhquE4dEREREREBL9E6dURERERERFSzmNQRERERERFpsBp7p+7p9deeZfny5TUVBhERERER0SutxpK6M2fOVKqeTCarqRCIiIiIiIheeTWW1B0+fLimmiYiIiIiIqL/w3fqiIiIiIiINFitrVN3+vRpbNu2DWlpaSgqKlI5tn379toKg4iIiIiI6JVSKyN1ERER6N69Oy5evIgdO3bgyZMnuHDhAg4dOgQTE5PaCIGIiIiIiOiVVCtJ3aJFi/DDDz9gz5490NHRwY8//ohLly5h+PDhsLW1rY0QiIiIiIiIXkm1ktSlpqZiwIABAAAdHR0UFBRAJpNh+vTpWL9+fW2EQERERERE9EqqlaTujTfewP379wEATZo0QVJSEgDg3r17ePjwYW2EQERERERE9EqqlYlSevbsiaioKDg4OGDYsGGYNm0aDh06hKioKPTp06c2QiAiIiIiInol1UpSt2rVKjx+/BgAMGfOHGhra+PEiRMYOnQovvrqq9oIgYiIiIiI6JUkE0KIug6CSuTn58PExAR5eXkwNjau63CIiIiIiKiOVCU3qJV36tzd3REeHo78/PzauBwREREREdFro1aSuvbt2yMwMBAWFhYYNmwYdu3ahSdPntTGpYmIiIiIiF5ptZLU/fjjj0hPT8fOnTthaGgIb29vmJubY+LEiTh69GhthEBERERERPRKqpN36h4/fow9e/Zg4cKFOH/+PBQKRW2H8FLiO3VERERERARULTeoldkvn5aVlYWIiAj8+uuvOHfuHJydnWs7BCIiIiIioldGrTx+mZ+fj7CwMLz77ruwsbHBTz/9hEGDBiElJQVxcXG1EQIRERER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fx9ASdKnra2tct3Lly8jLS1Nuq6rqyvOnz+vMk1rVFQUjI2N0a5du+fqLxERERER0bPU6EidQqFQmfWxXr16qF+//gu16e/vDx8fH3Tp0gXOzs5YsWIFCgoKpPUhvL290aRJEyxevFjlvJCQEAwePBiNGjUq02ZkZCTMzMxga2uL8+fPY9q0aRg8eDD69u0LoCTZ8/X1hb+/Pxo2bAhjY2N89tlncHV1Rbdu3QAAffv2Rbt27fDxxx9jyZIlyMrKwldffYUpU6ZwNI6IiIiIiGpMjSZ1QgiMGTNGSmoeP36MyZMnSzNPltq+fXul2xwxYgRyc3Mxb948ZGVloWPHjjhw4IA0KUlaWlqZd+EuX76MmJgY/PHHH2rbzMzMhL+/P7Kzs2FpaQlvb+8yC0T+8MMP0NLSwtChQ1FYWAgPDw+VRQ7lcjl+//13fPLJJ3B1dYWhoSF8fHzUPu5JRERERERUXWp0nbpnra5eKiwsrKZC0Chcp46IiIiIiICq5QY1OlLHZI2IiIiIiKhm1eri40RERERERFS9mNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGoxJHRERERERkQZjUkdERERERKTBmNQRERERERFpMCZ1REREREREGkwjk7rVq1ejadOm0NPTg4uLC+Lj48ut6+bmBplMVmYbMGCAVOfBgwfw8/ODtbU19PX10a5dO6xdu1Y6fv36dbVtyGQyREZGSvXUHY+IiKiZL4GIiIiIiAhAvboOoKq2bt0Kf39/rF27Fi4uLlixYgU8PDxw+fJlNG7cuEz97du3o6ioSNq/ffs2OnTogGHDhkll/v7+OHToEH799Vc0bdoUf/zxBz799FNYWVlh0KBBsLGxQWZmpkq769evx/fff49+/fqplIeFhcHT01Pab9CgQTX1nIiIiIiIqCyNG6lbvnw5JkyYgLFjx0ojagYGBggNDVVbv2HDhrCwsJC2qKgoGBgYqCR1J06cgI+PD9zc3NC0aVNMnDgRHTp0kEYA5XK5ShsWFhbYsWMHhg8fjvr166tcr0GDBir19PT0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",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2998e54-4bd2-432d-8948-ac57abe31640",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "USE_LEXICAL_SEARCH = True\n",
    "LEXICAL_SEARCH_K = 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "743b6854-6a92-4201-884f-7bf83ac6bfe6",
   "metadata": {},
   "source": [
    "It looks like lexical search improved our retrieval scores, which was expected since more chunks are added into the context. But it didn't really help improve our quality scores, perhaps the additional chunks added also introduced quite a bit of noise.\n",
    "\n",
    "**Note**: This was just one aspect (keyword matching) of lexical search that we explored but there are many other useful features such as filtering, counts, etc. It's also worth exploring how we combine the lexical search results with semantic search results."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "140e2b7f-f8a1-4d9c-a479-f9eefbd21451",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Reranking"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd99d5da-b9ee-40e2-99b8-3b43f2820618",
   "metadata": {},
   "source": [
    "So far with all of our approaches, we've used an embedding model (+ lexical search) to identify the top k relevant chunks in our dataset. The number of chunks (k) has been a small number because we found that adding too many chunks did not help and our LLMs have restricted context lengths. However, this was all under the assumption that the top k retrieved chunks were truly the most relevant chunks and that their order was correct as well. What if increasing the number of chunks didn't help because some relevant chunks were much lower in the ordered list. And, semantic representations, while very rich, were not trained for this specific task. \n",
    "\n",
    "In this section, we'll implement reranking so that we can use our semantic and lexical search methods to cast a much wider net over our dataset (retrieve many chunks) and then rerank the order based on the user's query. The intuition here is that we can account for gaps in our semantic representations with ranking specific to our use case. We'll train a supervised model that predicts which part of our [documentation](https://docs.ray.io/) is most relevant for a given user's query. We'll use this prediction to then rerank the relevant chunks so that chunks from this part of our documentation are moved to the top of the list.\n",
    "\n",
    "**Note**: We also experimented with [cross-encoders](https://www.sbert.net/docs/pretrained_cross-encoders.html) which processes the query and the relevant contexts together with the same model. This allows for a more contextual representation compared to cosine distance but it's also more computationally expensive. So we followed a similar approach of using the similarity distance first to extract many chunks and then use the cross-encoder to rerank and choose the top k chunks. Unfortunately, this approach didn't improve our quality as the technique below does but it's worth fine-tuning our cross-encoder using our synthetic embedding QA dataset.\n",
    "\n",
    "<img width=\"800\" src=\"https://images.ctfassets.net/xjan103pcp94/4bmoRNSzxtOyfToCtl68xq/d9727c41a3d435d1821eea5ab67c1e97/rag-based-llm-applications-reranking.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da391621-c587-4453-b365-6f25a049b3e3",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1eba3ee9-fe06-4a2d-998f-d51cdd075cd7",
   "metadata": {},
   "source": [
    "We're going to reuse the QA dataset we created in our fine-tuning section because that dataset has questions that map with specific sections."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83c3d888-1f71-4561-8d9c-787ae03a5e3b",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from collections import Counter\n",
    "import pandas as pd\n",
    "from sklearn.model_selection import train_test_split\n",
    "from tqdm import tqdm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "964f649e-8ba2-4866-b6a5-af5df2d1e17f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def get_tag(url):\n",
    "    return re.findall(r\"docs\\.ray\\.io/en/master/([^/]+)\", url)[0].split(\"#\")[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9f82ad86-e310-4ba4-b351-c513cf78c0ab",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>question</th>\n",
       "      <th>source</th>\n",
       "      <th>tag</th>\n",
       "      <th>section</th>\n",
       "      <th>text</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>3170</th>\n",
       "      <td>What is the purpose of the RayTrainReportCall...</td>\n",
       "      <td>https://docs.ray.io/en/master/train/api/doc/ra...</td>\n",
       "      <td>train</td>\n",
       "      <td>ray.train.lightning.RayTrainReportCallback.tea...</td>\n",
       "      <td>ray.train.lightning.RayTrainReportCallback.tea...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3099</th>\n",
       "      <td>What is the approximate number of rows that w...</td>\n",
       "      <td>https://docs.ray.io/en/master/data/api/doc/ray...</td>\n",
       "      <td>data</td>\n",
       "      <td>ray.data.Dataset.random_sample.html#ray-data-d...</td>\n",
       "      <td>ray.data.Dataset.random_sample.html#ray-data-d...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1216</th>\n",
       "      <td>What is the purpose of the 'value' parameter ...</td>\n",
       "      <td>https://docs.ray.io/en/master/ray-core/api/doc...</td>\n",
       "      <td>ray-core</td>\n",
       "      <td>ray.runtime_env.RuntimeEnvConfig.fromkeys.html...</td>\n",
       "      <td>ray.runtime_env.RuntimeEnvConfig.fromkeys.html...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5215</th>\n",
       "      <td>What is the purpose of the 'returns_to_go' va...</td>\n",
       "      <td>https://docs.ray.io/en/master/rllib/package_re...</td>\n",
       "      <td>rllib</td>\n",
       "      <td>ray.rllib.policy.sample_batch.SampleBatch.RETU...</td>\n",
       "      <td>ray.rllib.policy.sample_batch.SampleBatch.RETU...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5107</th>\n",
       "      <td>What is the purpose of the \"full_fetch\" param...</td>\n",
       "      <td>https://docs.ray.io/en/master/rllib/package_re...</td>\n",
       "      <td>rllib</td>\n",
       "      <td>ray.rllib.algorithms.algorithm.Algorithm.compu...</td>\n",
       "      <td>ray.rllib.algorithms.algorithm.Algorithm.compu...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                               question  \\\n",
       "3170   What is the purpose of the RayTrainReportCall...   \n",
       "3099   What is the approximate number of rows that w...   \n",
       "1216   What is the purpose of the 'value' parameter ...   \n",
       "5215   What is the purpose of the 'returns_to_go' va...   \n",
       "5107   What is the purpose of the \"full_fetch\" param...   \n",
       "\n",
       "                                                 source       tag  \\\n",
       "3170  https://docs.ray.io/en/master/train/api/doc/ra...     train   \n",
       "3099  https://docs.ray.io/en/master/data/api/doc/ray...      data   \n",
       "1216  https://docs.ray.io/en/master/ray-core/api/doc...  ray-core   \n",
       "5215  https://docs.ray.io/en/master/rllib/package_re...     rllib   \n",
       "5107  https://docs.ray.io/en/master/rllib/package_re...     rllib   \n",
       "\n",
       "                                                section  \\\n",
       "3170  ray.train.lightning.RayTrainReportCallback.tea...   \n",
       "3099  ray.data.Dataset.random_sample.html#ray-data-d...   \n",
       "1216  ray.runtime_env.RuntimeEnvConfig.fromkeys.html...   \n",
       "5215  ray.rllib.policy.sample_batch.SampleBatch.RETU...   \n",
       "5107  ray.rllib.algorithms.algorithm.Algorithm.compu...   \n",
       "\n",
       "                                                   text  \n",
       "3170  ray.train.lightning.RayTrainReportCallback.tea...  \n",
       "3099  ray.data.Dataset.random_sample.html#ray-data-d...  \n",
       "1216  ray.runtime_env.RuntimeEnvConfig.fromkeys.html...  \n",
       "5215  ray.rllib.policy.sample_batch.SampleBatch.RETU...  \n",
       "5107  ray.rllib.algorithms.algorithm.Algorithm.compu...  "
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Load data\n",
    "from pathlib import Path\n",
    "df = pd.read_json(Path(ROOT_DIR, \"datasets\", \"embedding_qa.json\"))\n",
    "df[\"tag\"] = df.source.map(get_tag)\n",
    "df[\"section\"] = df.source.map(lambda source: source.split(\"/\")[-1])\n",
    "df[\"text\"] = df[\"section\"] + \" \" + df[\"question\"]\n",
    "df.sample(n=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d264d8c6-83df-4eb2-90b8-aaec0b9c71e0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'rllib': 1269,\n",
       "         'tune': 979,\n",
       "         'train': 697,\n",
       "         'cluster': 690,\n",
       "         'data': 652,\n",
       "         'ray-core': 557,\n",
       "         'serve': 302,\n",
       "         'ray-observability': 175,\n",
       "         'ray-contribute': 95,\n",
       "         'workflows': 82,\n",
       "         'ray-air': 74,\n",
       "         'ray-more-libs': 66,\n",
       "         'ray-overview': 46,\n",
       "         'rllib-env.html': 17,\n",
       "         'installation.html': 16,\n",
       "         'tune.html': 5,\n",
       "         'joblib.html': 3,\n",
       "         'ray-references': 2})"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Counter(df.tag)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4bca87cb-dbea-47f7-9d0a-e2c84d1ff75b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({'rllib': 1269,\n",
       "         'tune': 979,\n",
       "         'train': 697,\n",
       "         'cluster': 690,\n",
       "         'data': 652,\n",
       "         'ray-core': 557,\n",
       "         'other': 406,\n",
       "         'serve': 302,\n",
       "         'ray-observability': 175})"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Map only what we want to keep\n",
    "tags_to_keep = [\"rllib\", \"tune\", \"train\", \"cluster\", \"ray-core\", \"data\", \"serve\", \"ray-observability\"]\n",
    "df[\"tag\"] = df.tag.apply(lambda x: x if x in tags_to_keep else \"other\")\n",
    "Counter(df.tag)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f31817a0-7bc7-42fe-8923-b3649d965d96",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Train and test data splits\n",
    "test_size = 0.2\n",
    "train_df, test_df = train_test_split(df, stratify=df.tag, test_size=test_size, random_state=1234)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da73dbc5-bb15-414a-9df0-57d25745e960",
   "metadata": {},
   "source": [
    "## Preprocessing"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1dbbbf23-9172-4865-8785-e248e523405a",
   "metadata": {},
   "source": [
    "We'll start by creating some preprocessing functions to better represent our data. For example, our documentation has many variables that are camel cased (ex. `RayDeepSpeedStrategy`). When a tokenizer is used on this, we often lose the individual tokens that we know to be useful and, instead, random subtokens are created.\n",
    "\n",
    "**Note**: we didn't omnisciently know to create these unique preprocessing functions! This is all a result of methodical iteration. We train a model → view incorrect data points → view how the data was represented (ex. subtokenization) → update preprocessing → iterate ↺"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22c56a71-fad6-4393-bee8-fe44f835cbf1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import re\n",
    "from transformers import BertTokenizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6fe5a48a-4a1a-43c6-9a69-a72fa03f357f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def split_camel_case_in_sentences(sentences):\n",
    "    def split_camel_case_word(word):\n",
    "        return re.sub(\"([a-z0-9])([A-Z])\", r\"\\1 \\2\", word)\n",
    "    processed_sentences = []\n",
    "    for sentence in sentences:\n",
    "        processed_words = []   \n",
    "        for word in sentence.split():\n",
    "            processed_words.extend(split_camel_case_word(word).split())\n",
    "        processed_sentences.append(\" \".join(processed_words))\n",
    "    return processed_sentences"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3485909-e074-4796-bf4b-ed388f56c61f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Tokenizer\n",
    "tokenizer = BertTokenizer.from_pretrained(\"bert-base-uncased\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9ddbe8bf-a974-4d84-b452-d15ba39e762f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def preprocess(texts):\n",
    "    texts = [re.sub(r'(?<=\\w)([?.,!])(?!\\s)', r' \\1', text) for text in texts]\n",
    "    texts = [text.replace(\"_\", \" \").replace(\"-\", \" \").replace(\"#\", \" \").replace(\".html\", \"\").replace(\".\", \" \") for text in texts]\n",
    "    texts = split_camel_case_in_sentences(texts)  # camelcase\n",
    "    texts = [tokenizer.tokenize(text) for text in texts]  # subtokens\n",
    "    texts = [\" \".join(word for word in text) for text in texts]\n",
    "    return texts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0d610206-5376-4cf4-af50-e5121a14c45a",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['ray deep speed strategy']\n",
      "['what is the default batch size for map batch ##es ?']\n"
     ]
    }
   ],
   "source": [
    "print (preprocess([\"RayDeepSpeedStrategy\"]))\n",
    "print (preprocess([\"What is the default batch_size for map_batches?\"]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea43347c-f025-4933-b358-6ad797e3d8e6",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9b6b234-4868-4b76-8f30-483513c59137",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.preprocessing import FunctionTransformer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be4f8194-5018-427d-86ed-bd1536cfcb58",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {color: black;}#sk-container-id-1 pre{padding: 0;}#sk-container-id-1 div.sk-toggleable {background-color: white;}#sk-container-id-1 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-1 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-1 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-1 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-1 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-1 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-1 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-1 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-1 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-1 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-1 div.sk-item {position: relative;z-index: 1;}#sk-container-id-1 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-1 div.sk-item::before, #sk-container-id-1 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-1 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-1 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-1 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-1 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-1 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-1 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-1 div.sk-label-container {text-align: center;}#sk-container-id-1 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-1 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;preprocess&#x27;,\n",
       "                 FunctionTransformer(func=&lt;function preprocess at 0x7fa070971bd0&gt;)),\n",
       "                (&#x27;vectorizer&#x27;, TfidfVectorizer()),\n",
       "                (&#x27;classifier&#x27;, LogisticRegression(multi_class=&#x27;multinomial&#x27;))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" ><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">Pipeline</label><div class=\"sk-toggleable__content\"><pre>Pipeline(steps=[(&#x27;preprocess&#x27;,\n",
       "                 FunctionTransformer(func=&lt;function preprocess at 0x7fa070971bd0&gt;)),\n",
       "                (&#x27;vectorizer&#x27;, TfidfVectorizer()),\n",
       "                (&#x27;classifier&#x27;, LogisticRegression(multi_class=&#x27;multinomial&#x27;))])</pre></div></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" ><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">FunctionTransformer</label><div class=\"sk-toggleable__content\"><pre>FunctionTransformer(func=&lt;function preprocess at 0x7fa070971bd0&gt;)</pre></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-3\" type=\"checkbox\" ><label for=\"sk-estimator-id-3\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">TfidfVectorizer</label><div class=\"sk-toggleable__content\"><pre>TfidfVectorizer()</pre></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-4\" type=\"checkbox\" ><label for=\"sk-estimator-id-4\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">LogisticRegression</label><div class=\"sk-toggleable__content\"><pre>LogisticRegression(multi_class=&#x27;multinomial&#x27;)</pre></div></div></div></div></div></div></div>"
      ],
      "text/plain": [
       "Pipeline(steps=[('preprocess',\n",
       "                 FunctionTransformer(func=<function preprocess at 0x7fa070971bd0>)),\n",
       "                ('vectorizer', TfidfVectorizer()),\n",
       "                ('classifier', LogisticRegression(multi_class='multinomial'))])"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Train classifier\n",
    "from rag.rerank import preprocess  # for pickle\n",
    "reranker = Pipeline([\n",
    "    (\"preprocess\", FunctionTransformer(preprocess)),\n",
    "    (\"vectorizer\", TfidfVectorizer(lowercase=True)),\n",
    "    (\"classifier\", LogisticRegression(multi_class=\"multinomial\", solver=\"lbfgs\"))\n",
    "])\n",
    "reranker.fit(train_df[\"text\"].tolist(), train_df[\"tag\"].tolist())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47825a4a-6b76-42ef-ad07-403710d25f97",
   "metadata": {},
   "source": [
    "**Note**: we also trained a BERT classifier and while performance was better than our logistic classifier, these large networks suffer from [overconfidence](https://arxiv.org/abs/1706.04599) and we can't use a threshold based approach as we do below. And without the threshold approach (where we only rerank when the reranker is truly confident), then the quality score of our application does not improve."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fd6ce6ca-ea56-4a27-af9d-758175170ebd",
   "metadata": {},
   "source": [
    "## Inference"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb1139d3-e43f-4bf4-8bac-43d3ee1ec30e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import pickle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a70ca619-0bf3-4254-9a94-77701ed2c771",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Save\n",
    "reranker_fp = Path(EFS_DIR, \"reranker.pkl\")\n",
    "with open(reranker_fp, \"wb\") as file:\n",
    "    pickle.dump(reranker, file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98d2450d-e035-4389-8236-b3553594972e",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Load\n",
    "reranker_fp = Path(EFS_DIR, \"reranker.pkl\")\n",
    "with open(reranker_fp, \"rb\") as file:\n",
    "    reranker = pickle.load(file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f8d1d74e-3ee7-445c-ab7d-386766f18b18",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def custom_predict(inputs, classifier, threshold=0.2, other_label=\"other\"):\n",
    "    y_pred = []\n",
    "    for item in classifier.predict_proba(inputs):\n",
    "        prob = max(item)\n",
    "        index = item.argmax()\n",
    "        if prob >= threshold:\n",
    "            pred = classifier.classes_[index]\n",
    "        else:\n",
    "            pred = other_label\n",
    "        y_pred.append(pred)\n",
    "    return y_pred"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "081e53d4-ba98-4323-85df-d5f03f1807f0",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'train'"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Test inference\n",
    "question = \"traning with deepspeed\"\n",
    "custom_predict([question], classifier=reranker)[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54a19983-1caf-4b11-8093-5e2a54cf7bbf",
   "metadata": {},
   "source": [
    "## Evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "987ee635-cdf0-490a-89bc-1795660b7cc2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import json\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import seaborn as sns\n",
    "from sklearn.metrics import confusion_matrix, precision_recall_fscore_support"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4baa30a5-2f44-4b6b-90a6-87b149b88bdc",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Evaluation\n",
    "metrics = {}\n",
    "y_test = test_df[\"tag\"]\n",
    "y_pred = custom_predict(inputs=test_df[\"text\"], classifier=reranker)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96b9bf23-2091-4b0a-bd0d-8dc13c03eb62",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 600x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Confusion matrix\n",
    "cm = confusion_matrix(y_test, y_pred)\n",
    "plt.figure(figsize=(6,4))\n",
    "sns.heatmap(cm, annot=True, fmt=\"d\", cmap=\"Blues\", \n",
    "            xticklabels=reranker.classes_, yticklabels=reranker.classes_)\n",
    "plt.ylabel(\"Actual\")\n",
    "plt.xlabel(\"Predicted\")\n",
    "plt.title(\"Confusion Matrix\")\n",
    "plt.xticks(rotation=65)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03057b05-ee59-47d8-b6ba-7b7f0b2c76d9",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "    \"precision\": 0.9168129573272782,\n",
      "    \"recall\": 0.9171029668411868,\n",
      "    \"f1\": 0.9154520876579969,\n",
      "    \"num_samples\": 1146.0\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "# Metrics\n",
    "overall_metrics = precision_recall_fscore_support(y_test, y_pred, average=\"weighted\")\n",
    "metrics[\"precision\"] = overall_metrics[0]\n",
    "metrics[\"recall\"] = overall_metrics[1]\n",
    "metrics[\"f1\"] = overall_metrics[2]\n",
    "metrics[\"num_samples\"] = np.float64(len(y_test))\n",
    "print (json.dumps(metrics, indent=4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40364870-7f40-457e-9bbd-bd51f8619022",
   "metadata": {},
   "source": [
    "Precision will be the most important metric here because we will only apply reranking if the softmax probability of our predicted tag is above the threshold value."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e2189b4-3b76-4770-b70e-f841a4133899",
   "metadata": {},
   "source": [
    "## Testing"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "def64f1e-3c02-498f-b233-92894abc1892",
   "metadata": {},
   "source": [
    "Besides just a metric based evaluation, we also want to assess how our model performs on some minimum functionality tests. We need all of these basic sanity checks to pass regardless of what type of model we use."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f6535156-fcc7-4bac-a1ba-4628171e7d27",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def predict_proba(question, classifier):\n",
    "    y_prob = classifier.predict_proba([question])\n",
    "    zipped = list(zip(y_prob[0], classifier.classes_))\n",
    "    return sorted(zipped, key=lambda x: x[0], reverse=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70cf7edb-7eb9-4692-9b60-57cea8aa0ede",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[85% train]: How to train a train an LLM using DeepSpeed? → ['how to train a train an ll ##m using deep speed ?']\n",
      "[67% serve]: How does autoscaling work in a Ray Serve application? → ['how does auto ##sca ##ling work in a ray serve application ?']\n",
      "[38% tune]: How to find the best checkpoint from the trial directory? → ['how to find the best checkpoint from the trial directory ?']\n",
      "[88% data]: How do I avoid my dataset shuffling during a ray.data.map_batches? → ['how do i avoid my data ##set shuffling during a ray data map batch ##es ?']\n",
      "[37% ray-core]: how to push a custom module to ray which is using by Actor? → ['how to push a custom module to ray which is using by actor ?']\n",
      "[29% other]: How would you compare Spark, Ray, Dask? → ['how would you compare spark , ray , das ##k ?']\n",
      "[59% ray-observability]: How do I enable Ray debug logs? → ['how do i enable ray de ##bu ##g logs ?']\n",
      "[74% rllib]: How do I set a maximum episode length when training with Rllib → ['how do i set a maximum episode length when training with r ##lli ##b']\n"
     ]
    }
   ],
   "source": [
    "# Basic tests\n",
    "tests = [\n",
    "    {\"question\": \"How to train a train an LLM using DeepSpeed?\", \"tag\": \"train\"},\n",
    "    {\"question\": \"How does autoscaling work in a Ray Serve application?\", \"tag\": \"serve\"},\n",
    "    {\"question\": \"How to find the best checkpoint from the trial directory?\", \"tag\": \"tune\"},\n",
    "    {\"question\": \"How do I avoid my dataset shuffling during a ray.data.map_batches?\", \"tag\": \"data\"},\n",
    "    {\"question\": \"how to push a custom module to ray which is using by Actor?\", \"tag\": \"ray-core\"},\n",
    "    {\"question\": \"How would you compare Spark, Ray, Dask?\", \"tag\": \"other\"},\n",
    "    {\"question\": \"How do I enable Ray debug logs?\", \"tag\": \"ray-observability\"},\n",
    "    {\"question\": \"How do I set a maximum episode length when training with Rllib\", \"tag\": \"rllib\"}]\n",
    "for test in tests:\n",
    "    question = test[\"question\"]\n",
    "    prob, pred = predict_proba(question=test[\"question\"], classifier=reranker)[0]\n",
    "    print (f\"[{prob*100:.0f}% {pred}]: {question} → {preprocess([question])}\")\n",
    "    assert (pred == test[\"tag\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f640f3ee-bc42-4f96-a0fb-f11c4015f2b4",
   "metadata": {},
   "source": [
    "## Experiment"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd7be07c-fee9-4b04-bb31-d58e07a17700",
   "metadata": {},
   "source": [
    "Now we're ready to apply our reranking model post retrieval using these steps:\n",
    "1. Increase the retrieved context (can experiment with this) so that we can apply reranking to yield a smaller subset (`num_chunks`). The intuition here is that we'll use semantic and lexical search to retrieve N chunks (N > k) and then we'll use reranking to reorder the retrieved results (top k).\n",
    "2. Perform generation using the top k retrieved chunks."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99ce7c04-9da0-43e3-b5db-2c59aff3fe87",
   "metadata": {},
   "source": [
    "We're going to alter our `QueryAgent` class directly to include reranking:\n",
    "\n",
    "```python\n",
    "class QueryAgent():\n",
    "    def __call__(rerank_threshold=0.3, rerank_k=7, **kwargs):\n",
    "        # Rerank\n",
    "        if self.reranker:\n",
    "            predicted_tag = custom_predict(\n",
    "                inputs=[query], classifier=self.reranker, threshold=rerank_threshold)[0]\n",
    "            if predicted_tag != \"other\":\n",
    "                sources = [item[\"source\"] for item in context_results]\n",
    "                reranked_indices = get_reranked_indices(sources, predicted_tag)\n",
    "                context_results = [context_results[i] for i in reranked_indices]\n",
    "            context_results = context_results[:rerank_k]\n",
    "```\n",
    "\n",
    "And the rerank function (along with other utilities specific to reranking) will now be placed in `rerank.py`:\n",
    "```python\n",
    "def get_reranked_indices(sources, predicted_tag):\n",
    "    tags = [get_tag(source) for source in sources]\n",
    "    reranked_indices = sorted(range(len(tags)), \n",
    "        key=lambda i: (tags[i] != predicted_tag, i))\n",
    "    return reranked_indices\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61f82f9f-694d-4c49-b39d-8cab4c285b13",
   "metadata": {},
   "source": [
    "And with that, let's use our query agent augmented with reranking on an evaluation run."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d2713153-da91-46e3-b1c7-5d06903d550d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "experiment_names = []\n",
    "rerank_threshold_list = [0, 0.3, 0.5, 0.7, 0.9]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "55f34f33-3764-49f1-8aed-7536c426ef9b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "application/json": {
       "generated_answer": " To kill a specific serve replica, you can use the following steps:\n\n1. Get the handle to the replica using `ray.get_actor()` function with the NAME of the replica from the output of `ray list actors` command.\n2. Use `ray.kill()` function to kill the replica, passing the replica handle as an argument. You can also specify `no_restart=True` to prevent the replica from automatically restarting.\n\nHere is an example:\n```python\nimport ray\n\n# Get the handle to the replica\nreplica_handle = ray.get_actor(\"SERVE_REPLICA::SleepyPid#RlRptP\", namespace=\"serve\")\n\n# Kill the replica\nray.kill(replica_handle, no_restart=True)\n```\nNote: While the replica is restarted, the other replicas can continue processing requests. Eventually, the replica restarts and continues serving requests.",
       "question": "how do I kill a specific serve replica",
       "reasoning": "The generated answer is detailed, accurate, and provides a step-by-step guide on how to kill a specific serve replica. It even includes a code example for better understanding. The reference answer does not provide any useful information, making the generated answer superior.",
       "reference_answer": "The context does not provide information on how to kill a specific serve replica.",
       "score": 5,
       "sources": [
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/advanced-guides/dev-workflow.html#local-development-with-http-requests",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-deployments",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#deployment-replica-failure",
        "https://docs.ray.io/en/master/serve/scaling-and-resource-allocation.html#autoscaling",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#worker-node-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/api/index.html#delete-api-serve-applications",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#replica-health-checking",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#httpproxy-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#serve-controller-failure",
        "https://docs.ray.io/en/master/serve/production-guide/fault-tolerance.html#head-node-failure"
       ]
      },
      "text/plain": [
       "<IPython.core.display.JSON object>"
      ]
     },
     "metadata": {
      "application/json": {
       "expanded": false,
       "root": "root"
      }
     },
     "output_type": "display_data"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 177/177 [17:03<00:00,  5.78s/it]\n"
     ]
    }
   ],
   "source": [
    "# Experiment\n",
    "use_reranking = True\n",
    "num_chunks = 30  # increase number of chunks\n",
    "rerank_k = NUM_CHUNKS + LEXICAL_SEARCH_K\n",
    "for rerank_threshold in rerank_threshold_list:\n",
    "    experiment_name = f\"rerank-{rerank_threshold}\"\n",
    "    experiment_names.append(experiment_name)\n",
    "    run_experiment(\n",
    "        experiment_name=experiment_name, \n",
    "        chunk_size=CHUNK_SIZE, \n",
    "        chunk_overlap=CHUNK_OVERLAP, \n",
    "        num_chunks=num_chunks,\n",
    "        embedding_model_name=EMBEDDING_MODEL_NAME,\n",
    "        embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "        llm=LLM,\n",
    "        evaluator=EVALUATOR,\n",
    "        docs_dir=DOCS_DIR, \n",
    "        experiments_dir=EXPERIMENTS_DIR, \n",
    "        references_fp=REFERENCES_FILE_PATH,\n",
    "        use_lexical_search=USE_LEXICAL_SEARCH,\n",
    "        lexical_search_k=LEXICAL_SEARCH_K,\n",
    "        use_reranking=use_reranking,\n",
    "        rerank_threshold=rerank_threshold,\n",
    "        rerank_k=rerank_k,\n",
    "        num_samples=NUM_SAMPLES)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "166b11ce-df5b-46a4-949c-16d7df45ab6c",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rerank-0\n",
      "  retrieval score: 0.7457627118644068\n",
      "  quality score: 3.9124293785310735\n",
      "\n",
      "rerank-0.3\n",
      "  retrieval score: 0.7570621468926554\n",
      "  quality score: 3.906779661016949\n",
      "\n",
      "rerank-0.5\n",
      "  retrieval score: 0.7740112994350282\n",
      "  quality score: 3.9915254237288136\n",
      "\n",
      "rerank-0.7\n",
      "  retrieval score: 0.7853107344632768\n",
      "  quality score: 3.9858757062146895\n",
      "\n",
      "rerank-0.9\n",
      "  retrieval score: 0.7853107344632768\n",
      "  quality score: 4.022598870056497\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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cjdw4EBs27YNmzZtgpubG7777jvs3bsXLi4uLTo/EREREVFrEglPPjDTBtavX4///ve/KCgogJubG9auXYt+/frVW9fLywtHjx6tU/7GG2/gp59+AgBMmjQJsbGxCsd9fX0RHx/fpHhKSkpgYmKC4uJiGBsbK3k1RERERET0olAmN2jzBS127NiB2bNnIyoqCv3798enn34KX19fXLp0CZaWlnXq7969G1VVVfL9u3fvws3NDaNHj1ao5+fnh82bN8v3dXV11XcRRERERETU7rX5tMDIyEhMnToVwcHBcHZ2RlRUFPT19RETE1NvfTMzM0gkEvl28OBB6Ovr10mudHV1Feq99NJLrXE5RERERETUTrVpclVVVYWTJ0/C29tbXqahoQFvb2+kpKQ0qY/o6GiMHTu2zkp4R44cgaWlJXr06IHp06fj7t27DfZRWVmJkpIShY2IiIiIiEgZbZpc3blzB1KpFGKxWKFcLBajoKDgqe1TU1ORlZWFf/7znwrlfn5++Oqrr5CQkICIiAgcPXoUw4cPb3BBhfDwcJiYmMg3Gxub5l8UERERERG1S23+zFVLREdHw9XVtc7iF2PHjpV/7Orqip49e8LOzg5HjhzB0KFD6/QTFhaG2bNny/dLSkqYYBERERERkVLadOTKwsICmpqaKCwsVCgvLCyERCJptG1ZWRm2b9+OKVOmPPU83bp1g4WFBa5cuVLvcV1dXRgbGytsREREREREymjT5EpHRwe9e/dGQkKCvEwmkyEhIQGenp6Ntt25cycqKysxbty4p57n999/x927d2FlZdXimImIiIiIiOrT5qsFzp49G1988QViY2Nx4cIFTJ8+HWVlZQgODgYATJgwAWFhYXXaRUdHw9/fH+bm5grlpaWlmDt3Ln777Tdcu3YNCQkJGDVqFOzt7eHr69sq10RERERERO1Pmz9zNWbMGNy+fRuLFi1CQUEBevXqhfj4ePkiF3l5edDQUMwBL126hOPHj+OXX36p05+mpibOnDmD2NhY3L9/H9bW1vDx8cHSpUv5risiIiIiIlIbkSAIQlsH8axR5i3MRERERET04lImN2jzaYFEREREREQvAiZXREREREREKsDkioiIiIiISAWYXBEREREREakAkysiIiIiIiIVYHJFRERERESkAkyuiIiIiIiIVIDJFRERERERkQowuSIiIiIi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",
      "text/plain": [
       "<Figure size 1000x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in experiment_names:\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR)\n",
    "plot_scores(scores=scores)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "65429a21-ee23-4d0a-9dee-60a4a4b09d09",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "original_num_chunks = NUM_CHUNKS\n",
    "NUM_CHUNKS = 30\n",
    "USE_RERANKING = True\n",
    "RERANK_THRESHOLD = 0.9\n",
    "RERANK_K = original_num_chunks + LEXICAL_SEARCH_K"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "628021f8-e79b-46e5-a8e0-ca7deb3be134",
   "metadata": {},
   "source": [
    "**Note**: there is still a lot more to experiment with reranking (increasing the initial `num_chunks`, adding lexical search resutls *after* reranking, weighted reranking where we promote the top N classes, etc.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4193ac04-bf77-4ae6-8273-a613b91f81a4",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('gpt-4-1106-preview',\n",
       "  {'retrieval_score': 0.7288135593220338, 'quality_score': 4.209039548022599}),\n",
       " ('rerank-0.9',\n",
       "  {'retrieval_score': 0.7853107344632768, 'quality_score': 4.022598870056497}),\n",
       " ('lexical-search-bm25-1',\n",
       "  {'retrieval_score': 0.7853107344632768, 'quality_score': 4.019774011299435})]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Top experiments (by quality and retrieval score)\n",
    "top_n = 3\n",
    "experiment_results = {}\n",
    "all_experiments = [d for d in Path(EXPERIMENTS_DIR, \"evaluations\").iterdir() if d.is_file()]\n",
    "for experiment_fp in all_experiments:\n",
    "    with open(str(experiment_fp), \"r\") as fp:\n",
    "        results = json.load(fp)\n",
    "    experiment_results[results[\"config\"][\"experiment_name\"]] = {\n",
    "        \"retrieval_score\": results[\"retrieval_score\"], \n",
    "        \"quality_score\": results[\"quality_score\"]}\n",
    "sorted(experiment_results.items(), key=lambda i: (i[1].get(\"quality_score\", float('inf')), i[1].get(\"retrieval_score\")), reverse=True)[:top_n]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f916ea05-4b99-4024-9211-ed35bb8ac9dc",
   "metadata": {
    "tags": [],
    "toc-hr-collapsed": true
   },
   "source": [
    "# Cost analysis"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c420359-c506-4257-b0d8-8b826134d001",
   "metadata": {},
   "source": [
    "Besides just performance, we also want to evaluate the cost of our configurations (especially given the high price points of larger LLMs). We’re going to break this down into prompt and sampled pricing. The prompt size is the number of characters in our system, assistant and user contents (which includes the retrieved contexts). And the sampled size is the number of characters the LLM generated in its response."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96c145a5-e97e-4811-ba12-c1a37d71c171",
   "metadata": {
    "tags": []
   },
   "source": [
    "**Note**: Our OSS models are served via [Anyscale Endpoints](https://endpoints.anyscale.com/)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f020340-9357-4a48-976b-cdac2e467351",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def cost_analysis(experiment_name):\n",
    "    eval_fp = Path(ROOT_DIR, EXPERIMENTS_DIR, \"evaluations\", f\"{experiment_name}_{EVALUATOR}.json\")\n",
    "    with open(eval_fp, \"r\") as fp:\n",
    "        d = json.load(fp)\n",
    "    num_samples = len(d[\"results\"])\n",
    "    prompt_size, sampled_size = 0, 0\n",
    "    for result in d[\"results\"]:\n",
    "        prompt_size += get_num_tokens(result[\"question\"]) + \\\n",
    "            ((CHUNK_SIZE/5)*(4/3) * original_num_chunks)  # 5 chars / word, 1 token = 3/4 word\n",
    "        sampled_size += get_num_tokens(result[\"generated_answer\"])\n",
    "    total_cost = PRICING[experiment_name][\"prompt\"]/1e6 * prompt_size + PRICING[experiment_name][\"sampled\"]/1e6 * sampled_size\n",
    "    avg_cost = total_cost / num_samples\n",
    "    \n",
    "    print (experiment_name)\n",
    "    print (f\"  prompted tokens (avg): {int(prompt_size/num_samples)}\")\n",
    "    print (f\"  sampled tokens (avg): {int(sampled_size/num_samples)}\")\n",
    "    print (f\"  total cost: ${total_cost:.4f}\")\n",
    "    print (f\"  avg cost: ${avg_cost:.4f}\")\n",
    "    print ()\n",
    "    return avg_cost"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee95574b-b355-4d5a-979b-467657bbd959",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gpt-3.5-turbo\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 73\n",
      "  total cost: $0.4761\n",
      "  avg cost: $0.0027\n",
      "\n",
      "gpt-4\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 111\n",
      "  total cost: $10.1884\n",
      "  avg cost: $0.0576\n",
      "\n",
      "gpt-4-1106-preview\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 200\n",
      "  total cost: $4.0641\n",
      "  avg cost: $0.0230\n",
      "\n",
      "llama-2-7b-chat-hf\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 248\n",
      "  total cost: $0.0516\n",
      "  avg cost: $0.0003\n",
      "\n",
      "llama-2-13b-chat-hf\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 226\n",
      "  total cost: $0.0851\n",
      "  avg cost: $0.0005\n",
      "\n",
      "llama-2-70b-chat-hf\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 218\n",
      "  total cost: $0.3388\n",
      "  avg cost: $0.0019\n",
      "\n",
      "codellama-34b-instruct-hf\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 187\n",
      "  total cost: $0.3333\n",
      "  avg cost: $0.0019\n",
      "\n",
      "mistral-7b-instruct-v0.1\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 135\n",
      "  total cost: $0.0486\n",
      "  avg cost: $0.0003\n",
      "\n",
      "mixtral-8x7b-instruct-v0.1\n",
      "  prompted tokens (avg): 1695\n",
      "  sampled tokens (avg): 180\n",
      "  total cost: $0.1661\n",
      "  avg cost: $0.0009\n",
      "\n"
     ]
    }
   ],
   "source": [
    "scores = {}\n",
    "for experiment_name in PRICING.keys():\n",
    "    scores[experiment_name] = print_experiment(experiment_name, EXPERIMENTS_DIR, verbose=False)\n",
    "    scores[experiment_name][\"average_cost\"] = cost_analysis(experiment_name=experiment_name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e58b0513-f630-47c2-a30f-aa900bfaa66c",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "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6bF/i34Cj8DoUuutG2aB/haFVzem6nX64WlMC5sG4b2xewnyn1Vd3WU1euj3KKopEREREX2CqjM3oIrjyO8nrJFK3UqtR1UnNy8XPld8CiW+AMSy5VeWIzcvt9B+IiIiIiKqfEx+P2EdmmlAR63uf8cJC5MA0FGriw7NNKoyLCqDqy+u4nnm82L3CxDwLPMZrr64WoVRERERERF9vpj8fsLk5STwcsz/6p73E+CCbS9HE8jLFZceU3X5J/OfSq1HREREREQVw+T3E9e7rQ42f9sO2mrSU5u11epi87ft0LutTjVFRiXRVNSs1HpERERERFQxtao7ACpd77Y66GmijSvxL/Ei7Q0aqeRPdeaI76erXaN20FLUwovMF0U+9yuBBFqKWmjXqF01REdERERE9PnhyG8NIS8ngXWLBuhv0RjWLRow8f3EycvJY3aH2QD+t7pzgYJtzw6ekJeTr/LYiIiIiIg+R0x+iT6SHvo9sLrLajRSbCRVrqWoxa85IiIiIiKqYpz2TPQR9dDvga56XXH1xVX8k/kPNBU10a5RO474EhERERFVMSa/RB+ZvJw8vtD+orrDICIiIiL6rHHaMxEREREREck8Jr9EREREREQk85j8EhERERERkcxj8ktEREREREQyj8kvERERERERyTwmv0RERERERCTz+FVHpRAEAQCQmppazZEQEREREVF1KsgJCnIEqlmY/JYiLS0NAKCnp1fNkRARERER0acgLS0Nampq1R0GlZNE4McWJcrLy8PTp0+hoqICiURS3eFQBaSmpkJPTw+PHz+GqqpqdYdDMor3GVUV3mtUFXifUVWoSfeZIAhIS0uDrq4u5OT4BGlNw5HfUsjJyaFJkybVHQZVIlVV1U/+DyvVfLzPqKrwXqOqwPuMqkJNuc844ltz8eMKIiIiIiIiknlMfomIiIiIiEjmMfmlz0adOnXg5eWFOnXqVHcoJMN4n1FV4b1GVYH3GVUF3mdUVbjgFREREREREck8jvwSERERERGRzGPyS0RERERERDKPyS8RERERERHJPCa/REREREREJPOY/JJM2Lx5M8zMzMQvR7e2tsbx48fL1DYwMBASiQQDBgz4uEFSjfch99nr16/h7u4OHR0d1KlTB8bGxggODq6iiKmm+pB7be3atWjZsiXq1asHPT09TJ06FW/evKmiiEkW+Pj4QCKR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2AUgIiKSMUx+iYioShkaGkIikeD27dtF7r99+zY0NTVRv359APnP/L6fKL/7PO/Fixfh5OSEPn364OjRo7h27Rp+/PFHZGdnVzjW7777DoGBgfj333+xe/duDB06FIqKiiW2qV27Njp16gRPT0/88ccfWLRoEby9vZGdnV1omveHUlJSktpOT0+Ho6MjoqOjpV737t1D586dK+WYRERENR2nPRMRUZVq0KABevbsiU2bNmHq1KlSCeGzZ88QEBAAd3d3sUxTUxOJiYni9r1795CZmSluX7hwAfr6+lKLZD18+LBcMSkoKCA3N7dQeZ8+faCkpITNmzfjxIkTOHv2bLn6BQATExO8ffsWb968gampKfLy8nDmzBlx2vO7WrdujUOHDkEQBHH0Nzw8HCoqKmjSpEmxx2jXrh0OHToEAwMD1KrF/9qJiIiKwpFfIiKqchs3bkRWVhbs7Oxw9uxZPH78GCdOnEDPnj1hbGyM+fPni3W7deuGjRs34tq1a4iMjISbmxtq164t7jcyMsKjR48QGBiI+/fvY/369Th8+HC54jEwMMDly5eRkJCApKQk5OXlAQDk5eXh4uKCOXPmwMjICNbW1iX206VLF2zduhVRUVFISEhAcHAwfvjhB3Tt2hWqqqowMDDAqFGj4OrqiqCgIMTHxyMsLAz79+8HAEyYMAGPHz/GpEmTEBcXh99++w1eXl6YNm2a+LxvUdzd3fHy5UsMHz4cERERuH//Pk6ePInRo0cXmdQTERF9jpj8EhFRlTMyMkJERASaN2+OIUOGQF9fH/b29jA2NkZ4eLi4+jEArFq1Cnp6eujUqRNGjBiBGTNmSE097tevH6ZOnYqJEyfCwsICFy5cwLx588oVz4wZMyAvLw8TExNoampKPS88ZswYZGdnY/To0aX2Y2dnB39/f/Tq1QutW7fGpEmTYGdnJya3ALB582YMGjQIEyZMQKtWrTB27FhkZGQAABo3bozg4GBcuXIF5ubmcHNzw5gxY6QW9CqKrq4uwsPDkZubi169esHU1BRTpkxB/fr1S0yaiYiIPicSoTzfJUFERPSReHl5YfXq1Th16hS+/PLL6g5HdO7cOXTv3h2PHz+GlpZWdYdDREREH4jJLxERfTJ2796NlJQUTJ48udpHLLOysvDPP/9g1KhR0NbWRkBAQLXGQ0RERBXD5JeIiKgIfn5+GDNmDCwsLHDkyBE0bty4ukMiIiKiCmDyS0RERERERDKPq2AQERERERGRzGPyS0RERERERDKPyS8RERERERHJPCa/REREREREJPOY/BIREREREZHMY/JLREREREREMo/JLxEREREREck8Jr9EREREREQk85j8EhERERERkcz7f6hxzn6+EGLXAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1000x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Prepare data for plotting\n",
    "experiment_names = list(scores.keys())\n",
    "average_costs = [scores[experiment_name][\"average_cost\"] for experiment_name in experiment_names]\n",
    "quality_scores = [scores[experiment_name][\"quality_score\"] for experiment_name in experiment_names]\n",
    "sorted_data = sorted(zip(quality_scores, average_costs, experiment_names))\n",
    "\n",
    "# Plotting\n",
    "plt.figure(figsize=(10, 4))\n",
    "for i, (q_score, avg_cost, exp_name) in enumerate(sorted_data):\n",
    "    plt.scatter(q_score, avg_cost, label=exp_name)\n",
    "    ha = \"left\"  # Horizontal alignment\n",
    "    va = \"bottom\"  # Vertical alignment\n",
    "    offset = 0.02  # Small offset for labels\n",
    "    if i > 0 and abs(sorted_data[i - 1][0] - q_score) < offset: ha = \"left\"  # Check left neighbor\n",
    "    if i < len(sorted_data) - 1 and abs(sorted_data[i + 1][0] - q_score) < offset: ha = \"right\"  # Check right neighbor\n",
    "    plt.text(q_score, avg_cost, exp_name, ha=ha, va=va, )\n",
    "\n",
    "# Add labels and title\n",
    "plt.xlabel(\"Quality Score\")\n",
    "plt.ylabel(\"Average cost / query ($)\")\n",
    "plt.legend(title=\"Experiments\", loc=\"upper left\", fontsize=8)\n",
    "plt.yscale(\"log\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d03dabb0-5a77-42da-b6c7-a65e8a5920a7",
   "metadata": {},
   "source": [
    "**Note**: This cost analysis is performed with our original experiments before lexical search, reranking, etc. since we haven't run experiments with these improvements on the other OSS and closed source LLMs yet."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d223b361-4d14-4ea1-804b-0eb92eb1224e",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Routing"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2463c757-f54e-4905-bd8b-d00a32c2ec7d",
   "metadata": {},
   "source": [
    "It seems that the most performant LLM, `gpt-4-turbo`, is also very expensive. While our OSS LLM (`mixtral-8x7b-instruct-v0.1`) is very close in quality but ~25X more cost-effective."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "391b4116-4312-48ef-aa3d-2184d46ed64b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cost multiplier compared to mixtral-8x7b-instruct-v0.1\n",
      "  gpt-3.5-turbo: 2.87X\n",
      "  gpt-4: 61.35X\n",
      "  gpt-4-1106-preview: 24.47X\n",
      "  llama-2-7b-chat-hf: 0.31X\n",
      "  llama-2-13b-chat-hf: 0.51X\n",
      "  llama-2-70b-chat-hf: 2.04X\n",
      "  codellama-34b-instruct-hf: 2.01X\n",
      "  mistral-7b-instruct-v0.1: 0.29X\n",
      "  mixtral-8x7b-instruct-v0.1: 1.00X\n"
     ]
    }
   ],
   "source": [
    "# Cost multiplier\n",
    "chosen_model = LLM.split('/')[-1].lower()\n",
    "print (f\"Cost multiplier compared to {chosen_model}\")\n",
    "for model in scores:\n",
    "    print (f\"  {model}: {scores[model]['average_cost']/scores[chosen_model]['average_cost']:.2f}X\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29727066-6a67-4ff9-a4d4-b4517f5014c4",
   "metadata": {},
   "source": [
    "However, we want to be able to serve the most performant and cost-effective solution. We can close this gap in performance between open source and proprietary models by routing queries to the right model according to the complexity or topic of the query. For example, in our application, open source models perform really well on simple queries where the answer can be easily inferred from the retrieved context. However, the OSS models fall short for queries that involve reasoning, numbers or code examples. To identify the appropriate LLM to use, we can train a classifier that takes the query and routes it to the best model.\n",
    "\n",
    "<img width=\"800\" src=\"https://images.ctfassets.net/xjan103pcp94/7FWrvPPlIdz5fs8wQgxLFz/fdae368044275028f0544a3d252fcfe4/image15.png\">\n",
    "\n",
    "**Note**: In part 2 of this series, we’ll fine-tune our embedding models and OSS LLMs to make them even more performant.\n",
    "\n",
    "In order to implement this, we hand-annotated a [dataset of 1.8k queries](https://github.com/ray-project/llm-applications/blob/main/datasets/routing-dataset-train.jsonl) according to which model (`gpt-4` (label=0) or `codellama-34b` (label=1)) would be appropriate -- by default we route to OSS LLM and only if the query needs more advanced capabilities do we send the query to `gpt-4`. We then evaluate the performance of the model on a test set that has been scored with an evaluator."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a731efa-7990-4856-a9e0-250be556505b",
   "metadata": {},
   "source": [
    "## Dataset"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d506e529-518b-4b37-bd81-4d5e7532b528",
   "metadata": {},
   "source": [
    "Let's first train the model on the training dataset [routing-dataset-training.jsonl](https://github.com/ray-project/llm-applications/blob/main/datasets/routing-dataset-train.jsonl):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "86e4a927-ab91-4189-97d2-35bd045c0360",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "with open(Path(ROOT_DIR, \"datasets\", \"routing-dataset-train.jsonl\")) as f:\n",
    "    records = [json.loads(l) for l in f]\n",
    "    texts = [record[\"question\"] for record in records]\n",
    "    labels = [record[\"target\"] for record in records]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d9a4d97-a39b-409e-9b45-ae609c9f651b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question for gpt-4:\n",
      " {'question': 'if I am inside of a anyscale cluster how do I get my cluster-env-build-id', 'target': 0}\n",
      "\n",
      "Question for codellama-34b:\n",
      " {'question': 'what is num_samples in tune?', 'target': 1}\n"
     ]
    }
   ],
   "source": [
    "# Sample records (1 = can be handled by OSS LLM)\n",
    "print (\"Question for gpt-4:\\n\", [record for record in records if record[\"target\"] == 0][0]) \n",
    "print (\"\\nQuestion for OSS LLM:\\n\", [record for record in records if record[\"target\"] == 1][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea873f02-d4e4-4bfa-b583-7d750c1144ff",
   "metadata": {},
   "source": [
    "## Modeling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25404ad8-8300-4e6d-ac98-4117e4608128",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import pickle\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.feature_extraction.text import CountVectorizer\n",
    "from sklearn.linear_model import LogisticRegression"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3021c4fa-caf1-489d-90d5-cf1890214c0d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-10 {color: black;}#sk-container-id-10 pre{padding: 0;}#sk-container-id-10 div.sk-toggleable {background-color: white;}#sk-container-id-10 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-10 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-10 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-10 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-10 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-10 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-10 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-10 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-10 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-10 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-10 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-10 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-10 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-10 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-10 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-10 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-10 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-10 div.sk-item {position: relative;z-index: 1;}#sk-container-id-10 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-10 div.sk-item::before, #sk-container-id-10 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-10 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-10 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-10 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-10 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-10 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-10 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-10 div.sk-label-container {text-align: center;}#sk-container-id-10 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-10 div.sk-text-repr-fallback {display: none;}</style><div id=\"sk-container-id-10\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>Pipeline(steps=[(&#x27;vectorizer&#x27;, CountVectorizer()),\n",
       "                (&#x27;classifier&#x27;, LogisticRegression(multi_class=&#x27;multinomial&#x27;))])</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item sk-dashed-wrapped\"><div class=\"sk-label-container\"><div class=\"sk-label sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-32\" type=\"checkbox\" ><label for=\"sk-estimator-id-32\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">Pipeline</label><div class=\"sk-toggleable__content\"><pre>Pipeline(steps=[(&#x27;vectorizer&#x27;, CountVectorizer()),\n",
       "                (&#x27;classifier&#x27;, LogisticRegression(multi_class=&#x27;multinomial&#x27;))])</pre></div></div></div><div class=\"sk-serial\"><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-33\" type=\"checkbox\" ><label for=\"sk-estimator-id-33\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">CountVectorizer</label><div class=\"sk-toggleable__content\"><pre>CountVectorizer()</pre></div></div></div><div class=\"sk-item\"><div class=\"sk-estimator sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-34\" type=\"checkbox\" ><label for=\"sk-estimator-id-34\" class=\"sk-toggleable__label sk-toggleable__label-arrow\">LogisticRegression</label><div class=\"sk-toggleable__content\"><pre>LogisticRegression(multi_class=&#x27;multinomial&#x27;)</pre></div></div></div></div></div></div></div>"
      ],
      "text/plain": [
       "Pipeline(steps=[('vectorizer', CountVectorizer()),\n",
       "                ('classifier', LogisticRegression(multi_class='multinomial'))])"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Train classifier\n",
    "router = Pipeline([\n",
    "    (\"vectorizer\", CountVectorizer()),\n",
    "    (\"classifier\", LogisticRegression(multi_class=\"multinomial\", solver=\"lbfgs\"))\n",
    "])\n",
    "router.fit(texts, labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7570fc7-158b-4799-87fb-33c1298b9594",
   "metadata": {},
   "source": [
    "**Note**: we also trained a BERT classifier and performance was not as good as our logistic regression use case given the size of our dataset."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68dd8c1e-08b7-43ee-829a-f3c6c2e8d8b7",
   "metadata": {},
   "source": [
    "## Inference"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ca1ab5d-9a68-4062-a9f9-0dc267b1abe6",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Router file path\n",
    "router_fp = Path(EFS_DIR, \"router.pkl\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3b9169b-2534-4c55-bf4b-471a4e524ed9",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Save\n",
    "with open(router_fp, \"wb\") as file:\n",
    "    pickle.dump(router, file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82f07fc8-3cf6-4189-a8a4-593860a73acf",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Load\n",
    "with open(router_fp, \"rb\") as file:\n",
    "    router = pickle.load(file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57536c4d-34d8-48e1-9d86-b83a5d766409",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[OSS] What is the default batch size for map_batches?\n"
     ]
    }
   ],
   "source": [
    "# Inference\n",
    "router_map = {0: \"GPT-4\", 1: \"OSS\"}\n",
    "query = \"What is the default batch size for map_batches?\"\n",
    "print (f\"[{router_map[router.predict([query])[0]]}]\", query)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77cc5449-5cfc-469d-9f84-42f4ebff4f3d",
   "metadata": {},
   "source": [
    "## Evaluation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cc6a7b8-427b-433c-98e3-c82b6ffaf042",
   "metadata": {},
   "source": [
    "Now let's evaluate the performance on the [test dataset](https://github.com/ray-project/llm-applications/blob/main/datasets/routing-dataset-test.jsonl):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "073421c5-662c-4774-b256-103e11d81361",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pickle\n",
    "from sklearn.metrics import accuracy_score, classification_report\n",
    "from sklearn.metrics import precision_recall_fscore_support"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15b20f4e-57dc-42a5-9fce-012ce2c672e1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Load\n",
    "with open(router_fp, \"rb\") as file:\n",
    "    router = pickle.load(file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87181fbe-b56f-485e-8089-34a2b1de3f49",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "with open(Path(ROOT_DIR, \"datasets\", \"routing-dataset-test.jsonl\")) as f:\n",
    "    records = [json.loads(line) for line in f]\n",
    "    texts = [record[\"question\"] for record in records]\n",
    "    y_test = [record[\"target\"] for record in records]\n",
    "    score_test = [record[\"score\"] for record in records]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4c2b3f1d-be35-4103-9aeb-ae8d562b3ee2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Predictions\n",
    "y_pred = router.predict(texts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "beddd2f0-80a3-4c3d-a66c-7977fb1c43bc",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "    \"precision\": 0.9191264005602239,\n",
      "    \"recall\": 0.9285714285714286,\n",
      "    \"f1\": 0.9226432439812495,\n",
      "    \"num_samples\": 574.0\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "metrics = {}\n",
    "performance = precision_recall_fscore_support(y_test, y_pred, average=\"weighted\")\n",
    "metrics[\"precision\"] = performance[0]\n",
    "metrics[\"recall\"] = performance[1]\n",
    "metrics[\"f1\"] = performance[2]\n",
    "metrics[\"num_samples\"] = np.float64(len(y_test))\n",
    "print (json.dumps(metrics, indent=4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9deb50e5-5702-4740-a92c-602af580f572",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# total samples 574\n",
      "# samples for OSS models: 544 (94.8%)\n",
      "Performance on samples predicted for codellama/CodeLlama-34b-Instruct-hf: 3.87\n",
      "Performance on samples predicted for gpt-4: 3.55\n"
     ]
    }
   ],
   "source": [
    "print (\"# total samples\", len(y_pred))\n",
    "print(f\"# samples for OSS models: {sum(y_pred)} ({sum(y_pred)*100/len(y_pred):.1f}%)\")\n",
    "print(f\"Performance on samples predicted for {LLM}: {np.mean([score_test[i] for i, p in enumerate(y_pred) if p]):.2f}\")\n",
    "print(f\"Performance on samples predicted for gpt-4: {np.mean([score_test[i] for i, p in enumerate(y_pred) if not p]):.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec755df6-2568-445c-a9b0-7f3fa30fa9d4",
   "metadata": {},
   "source": [
    "**Note**: For our dataset, a small logistic regression model is good enough to perform the routing. But if your use case is more complex, consider training a more complex model, like a BERT-based classifier to perform the classification. These models are still small enough that wouldn’t introduce too much latency. Be sure to check out this [guide](https://github.com/GokuMohandas/Made-With-ML) if you want to learn how to train and deploy supervised deep learning models."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a84b35b1-1342-47cb-a203-b8f24926cfab",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Serving"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "014a7e88-a8c1-4114-ae2a-0651363595da",
   "metadata": {},
   "source": [
    "Now we're ready to start serving our Ray Assistant using our best configuration. We're going to use [Ray Serve](https://docs.ray.io/en/latest/serve/index.html) with [FastAPI](https://fastapi.tiangolo.com/) to develop and scale our service. First, we'll define some data structures like `Query` and `Answer` to represent the inputs and outputs to our service. We will also define a small function to load our index (assumes that the respective SQL dump file already exists). Finally, we can define our `QueryAgent` and use it to serve `POST` requests with the query. And we can serve our agent at any deployment scale we wish using the [@serve.deployment](https://docs.ray.io/en/latest/serve/api/doc/ray.serve.Deployment.html) decorator where we can specify the number of replicas, compute resources, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "31cbea09-318f-49b7-864c-ba7b8072f5be",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import pickle\n",
    "import requests\n",
    "from typing import List"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "123e53fa-8b11-4781-91fd-0947a242e0d5",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from fastapi import FastAPI\n",
    "from pydantic import BaseModel\n",
    "from ray import serve\n",
    "from rag.generate import QueryAgent\n",
    "from rag.index import load_index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cab7b615-d03b-4fb2-b099-3a02dd7e8c2f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Initialize application\n",
    "app = FastAPI()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6d7ab2c-4946-4c6e-92da-08450ebc5a68",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "class Query(BaseModel):\n",
    "    query: str"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00d51f9a-fff8-40d6-92d5-0d9984e311cc",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "class Answer(BaseModel):\n",
    "    question: str\n",
    "    sources: List[str]\n",
    "    answer: str\n",
    "    llm: str"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24aca110-8bff-4097-aaee-33a70043c73c",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "DeprecationWarning: `route_prefix` in `@serve.deployment` has been deprecated. To specify a route prefix for an application, pass it into `serve.run` instead.\n"
     ]
    }
   ],
   "source": [
    "@serve.deployment(route_prefix=\"/\", num_replicas=1, ray_actor_options={\"num_cpus\": 6, \"num_gpus\": 1})\n",
    "@serve.ingress(app)\n",
    "class RayAssistantDeployment:\n",
    "    def __init__(self, chunk_size, chunk_overlap, num_chunks, \n",
    "                 embedding_model_name, embedding_dim,\n",
    "                 use_lexical_search, lexical_search_k, \n",
    "                 use_reranking, rerank_threshold, rerank_k,\n",
    "                 llm, sql_dump_fp=None):\n",
    "        \n",
    "        # Set up\n",
    "        chunks = load_index(\n",
    "            embedding_model_name=embedding_model_name, \n",
    "            embedding_dim=embedding_dim, \n",
    "            chunk_size=chunk_size, \n",
    "            chunk_overlap=chunk_overlap,\n",
    "            sql_dump_fp=sql_dump_fp,\n",
    "        )\n",
    "\n",
    "        # Lexical index\n",
    "        lexical_index = None\n",
    "        self.lexical_search_k = lexical_search_k\n",
    "        if use_lexical_search:\n",
    "            texts = [re.sub(r\"[^a-zA-Z0-9]\", \" \", chunk[1]).lower().split() for chunk in chunks]\n",
    "            lexical_index = BM25Okapi(texts)\n",
    "\n",
    "        # Reranker\n",
    "        reranker = None\n",
    "        self.rerank_threshold = rerank_threshold\n",
    "        self.rerank_k = rerank_k\n",
    "        if use_reranking:\n",
    "            reranker_fp = Path(EFS_DIR, \"reranker.pkl\")\n",
    "            with open(reranker_fp, \"rb\") as file:\n",
    "                reranker = pickle.load(file)\n",
    "\n",
    "        # Query agent\n",
    "        self.num_chunks = num_chunks\n",
    "        system_content = \"Answer the query using the context provided. Be succinct. \" \\\n",
    "            \"Contexts are organized in a list of dictionaries [{'text': <context>}, {'text': <context>}, ...]. \" \\\n",
    "            \"Feel free to ignore any contexts in the list that don't seem relevant to the query. \"\n",
    "        self.oss_agent = QueryAgent(\n",
    "            embedding_model_name=embedding_model_name,\n",
    "            chunks=chunks,\n",
    "            lexical_index=lexical_index,\n",
    "            reranker=reranker,\n",
    "            llm=llm,\n",
    "            max_context_length=MAX_CONTEXT_LENGTHS[llm],\n",
    "            system_content=system_content)\n",
    "        self.gpt_agent = QueryAgent(\n",
    "            embedding_model_name=embedding_model_name,\n",
    "            chunks=chunks,\n",
    "            lexical_index=lexical_index,\n",
    "            reranker=reranker,\n",
    "            llm=\"gpt-4\",\n",
    "            max_context_length=MAX_CONTEXT_LENGTHS[\"gpt-4\"],\n",
    "            system_content=system_content)\n",
    "\n",
    "        # Router\n",
    "        router_fp = Path(EFS_DIR, \"router.pkl\")\n",
    "        with open(router_fp, \"rb\") as file:\n",
    "            self.router = pickle.load(file)\n",
    "\n",
    "    @app.post(\"/query\")\n",
    "    def query(self, query: Query) -> Answer:\n",
    "        use_oss_agent = self.router.predict([query.query])[0]\n",
    "        agent = self.oss_agent if use_oss_agent else self.gpt_agent\n",
    "        result = agent(\n",
    "            query=query.query, num_chunks=self.num_chunks, \n",
    "            lexical_search_k=self.lexical_search_k, \n",
    "            rerank_threshold=self.rerank_threshold, \n",
    "            rerank_k=self.rerank_k, \n",
    "            stream=False)\n",
    "        return Answer.parse_obj(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79d4b6eb-cdbc-46cb-ab3d-adc246f9da9c",
   "metadata": {},
   "source": [
    "**Note**: As we can see, Ray Serve makes [model composition](https://docs.ray.io/en/latest/serve/model_composition.html) extremely easy and we could continue to make this more fine-grained. For example, we can train a classifier to discern between queries for `mixtral-8x7b-instruct-v0.1`,` codellama-34b-instruct-hf` (for code generation) and `gpt-4` (for highly complex queries). Also, we can use streaming end-to-end to reduce the time a user has to wait for the answer. Check out the `/stream` method in `rag/serve.py`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80a097e8-2cbb-4193-87cd-eca48a143301",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[2m\u001b[36m(ServeController pid=213991)\u001b[0m WARNING 2023-11-09 22:10:03,839 controller 213991 application_state.py:663 - The deployments ['RayAssistantDeployment'] are UNHEALTHY.\n",
      "\u001b[2m\u001b[36m(ServeController pid=213991)\u001b[0m INFO 2023-11-09 22:10:03,942 controller 213991 deployment_state.py:1390 - Deploying new version of deployment RayAssistantDeployment in application 'default'.\n",
      "\u001b[2m\u001b[36m(ServeController pid=213991)\u001b[0m INFO 2023-11-09 22:10:04,044 controller 213991 deployment_state.py:1679 - Adding 1 replica to deployment RayAssistantDeployment in application 'default'.\n",
      "\u001b[2m\u001b[36m(ServeReplica:default:RayAssistantDeployment pid=217138)\u001b[0m Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n",
      "\u001b[2m\u001b[36m(ServeReplica:default:RayAssistantDeployment pid=217138)\u001b[0m Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n",
      "\u001b[2m\u001b[36m(ServeReplica:default:RayAssistantDeployment pid=217138)\u001b[0m INFO 2023-11-09 22:10:27,795 RayAssistantDeployment default#RayAssistantDeployment#ZuxuxM 2dacbeb8-f591-48d6-a4fe-b86043d7ceff / default replica.py:749 - __CALL__ OK 0.2ms\n",
      "2023-11-09 22:10:27,934\tINFO router.py:1132 -- Using router <class 'ray.serve._private.router.PowerOfTwoChoicesReplicaScheduler'>.\n",
      "2023-11-09 22:10:27,940\tINFO router.py:473 -- Got updated replicas for deployment 'RayAssistantDeployment' in application 'default': {'default#RayAssistantDeployment#ZuxuxM'}.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "RayServeSyncHandle(deployment='RayAssistantDeployment')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Deploy the Ray Serve application.\n",
    "deployment = RayAssistantDeployment.bind(\n",
    "    chunk_size=CHUNK_SIZE,\n",
    "    chunk_overlap=CHUNK_OVERLAP,\n",
    "    num_chunks=NUM_CHUNKS,\n",
    "    embedding_model_name=EMBEDDING_MODEL_NAME,\n",
    "    embedding_dim=EMBEDDING_DIMENSIONS[EMBEDDING_MODEL_NAME],\n",
    "    use_lexical_search=USE_LEXICAL_SEARCH,\n",
    "    lexical_search_k=LEXICAL_SEARCH_K, \n",
    "    use_reranking=USE_RERANKING, \n",
    "    rerank_threshold=RERANK_THRESHOLD, \n",
    "    rerank_k=RERANK_K,\n",
    "    llm=LLM)\n",
    "serve.run(deployment)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f37bd52-ee23-4451-950b-a4badc1c2e52",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'question': 'What is the default batch size for map_batches?',\n",
       " 'sources': ['https://docs.ray.io/en/master/data/batch_inference.html#configuring-batch-size',\n",
       "  'https://docs.ray.io/en/master/data/transforming-data.html#configuring-batch-size',\n",
       "  'https://docs.ray.io/en/master/data/examples/pytorch_resnet_batch_prediction.html#model-inference',\n",
       "  'https://docs.ray.io/en/master/data/api/doc/ray.data.Dataset.map_batches.html#ray-data-dataset-map-batches',\n",
       "  'https://docs.ray.io/en/master/data/batch_inference.html#configuring-batch-size',\n",
       "  'https://docs.ray.io/en/master/data/examples/huggingface_vit_batch_prediction.html#step-3-scaling-up-to-the-full-dataset-with-ray-data',\n",
       "  'https://docs.ray.io/en/master/serve/advanced-guides/dyn-req-batch.html#tips-for-fine-tuning-batching-parameters',\n",
       "  'https://docs.ray.io/en/master/data/api/doc/ray.data.Dataset.map_batches.html#ray-data-dataset-map-batches',\n",
       "  'https://docs.ray.io/en/master/data/examples/batch_inference_object_detection.html#model-inference'],\n",
       " 'answer': '  The default batch size for map_batches is 4096.',\n",
       " 'llm': 'codellama/CodeLlama-34b-Instruct-hf'}"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\u001b[2m\u001b[36m(ServeReplica:default:RayAssistantDeployment pid=217138)\u001b[0m INFO 2023-11-09 22:10:35,329 RayAssistantDeployment default#RayAssistantDeployment#ZuxuxM 380d5b6f-c3f8-43fa-a3c9-c22d5b04b47d /query default replica.py:749 - __CALL__ OK 3430.1ms\n"
     ]
    }
   ],
   "source": [
    "# Inference\n",
    "data = {\"query\": \"What is the default batch size for map_batches?\"}\n",
    "response = requests.post(\"http://127.0.0.1:8000/query\", json=data)\n",
    "response.json()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f0bc8bd-5d8a-4936-8460-8715464ed230",
   "metadata": {},
   "source": [
    "**Note**: As we can see, Ray Serve makes [model composition](https://docs.ray.io/en/latest/serve/model_composition.html) extremely easy and we could continue to make this even more fine-grained with more workflow logic.\n",
    "\n",
    "Once our application is served, we’re free to use it anywhere we want. For example, we use it as a bot on our Slack channels and as a widget on our docs page (public release coming soon). We can use this to collect feedback from our users to continually improve the application (fine-tuning, UI/UX, etc.).\n",
    "\n",
    "<img width=\"600\" src=\"https://images.ctfassets.net/xjan103pcp94/7pyW8T7La5T51C8iXEwmAO/706dc8ed0ca75cdcbf971d9e74cd67b3/Screenshot_2023-10-24_at_12.56.39_PM.png\">"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74566075-7ad8-4269-8be5-5dc2602ef06b",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2023-11-09 22:10:41,238\tINFO router.py:473 -- Got updated replicas for deployment 'RayAssistantDeployment' in application 'default': set().\n",
      "\u001b[2m\u001b[36m(ServeController pid=213991)\u001b[0m INFO 2023-11-09 22:10:41,235 controller 213991 deployment_state.py:1707 - Removing 1 replica from deployment 'RayAssistantDeployment' in application 'default'.\n",
      "\u001b[2m\u001b[36m(ServeController pid=213991)\u001b[0m INFO 2023-11-09 22:10:43,848 controller 213991 deployment_state.py:2027 - Replica default#RayAssistantDeployment#ZuxuxM is stopped.\n"
     ]
    }
   ],
   "source": [
    "# Shutdown\n",
    "serve.shutdown()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "101dab0a-5a8d-4156-b6d2-2f38ff333add",
   "metadata": {},
   "source": [
    "# Data flywheel"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d57ef4c-fa7c-491f-befe-f894ee036cab",
   "metadata": {},
   "source": [
    "Creating an application like this is not a one-time task. It's extremely important that we continue to iterate and keep our application up to date. This includes continually reindexing our data so that our application is working with the most up-to-date information. As well as rerunning our experiments to see if any of the decisions need to be altered. This process of continuous iteration can be achieved by mapping our workflows to [CI/CD pipelines](https://madewithml.com/courses/mlops/cicd/).\n",
    "\n",
    "A key part of iteration that goes beyond automated reindexing, evaluation, etc. involves fixing our data itself. In fact, we found that this is the **most** impactful lever (way beyond our retrieval and generation optimizations above) we could control. Here is an example workflow we've settled on:\n",
    "1. Users use the RAG application to ask questions about the product.\n",
    "2. Use feedback (👍/👎, visited source pages, top-k cosine scores, etc.) to identify underperforming queries.\n",
    "3. Inspect the retrieved resources, tokenization, etc. to decide if it's a shortcoming of retrieval, generation or the underlying data source.\n",
    "4. If something in the data can be improved, separated into sections/pages, etc. → fix it!\n",
    "5. Reindex and deploy a new version of the application."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf3f9a4a-15bb-4567-82bb-e2a0808f1616",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Impact"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "615b2ad0-94a1-477c-92ee-6bdab3dcc99e",
   "metadata": {},
   "source": [
    "## Products and productivity"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6df6c44c-10e5-482f-a715-efd0b2c068a1",
   "metadata": {},
   "source": [
    "Building an LLM application like this has had a tremendous impact on our products and company. There were expected 1st order impacts in overall developer and user adoption for our products. The capability to interact and solve problems that our users experience in a self-serve and immediate manner is the type of feature that would improve the experience of any product. It makes it significantly easier for people to succeed and it elevated the perception around LLM applications from a **nice-to-have** to a **must-have**. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fce8c9b5-cce0-467c-81b9-1aefcb7437d3",
   "metadata": {},
   "source": [
    "## Foundational agents"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e895c161-f035-49dc-84d5-1f70d2b65ef9",
   "metadata": {},
   "source": [
    "However, there were also some 2nd order impacts that we didn’t immediately realize. For example, when we further inspected user queries that yielded poor scores, often the issue existed because of a gap in our documentation. When we made the fix (ex. added the appropriate section to our docs), this improved our product and the LLM application itself — creating a very valuable feedback flywheel. Furthermore, when internal teams learned of the capabilities of our LLM application, this generated the development of highly valuable LLM applications that depend on this Ray docs LLM application as one of its **foundational agents** that it uses to perform its tasks."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c79bcbe-89eb-469d-a809-acb780cec143",
   "metadata": {},
   "source": [
    "<img width=\"700\" src=\"https://images.ctfassets.net/xjan103pcp94/2UF2tSV3kmXtrzmqMsYrLF/76bcc71b481986eb6cb3b06d60582ec5/image18.png\">"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc4b13ef-11a8-472d-85a5-48b480628e59",
   "metadata": {},
   "source": [
    "For example, we’ve internally developed a feature called Anyscale Doctor that helps developers diagnose and debug issues during development. Issues in code can be caused by a variety of reasons but when the issue is Ray related, the LLM application we built here is called to aid in resolving the particular issue."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c61ff8d-d76b-47d5-8ae8-85d7aaf41b19",
   "metadata": {},
   "source": [
    "# Learn more"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f451be7-3468-4dab-aed9-2e6621ad0a10",
   "metadata": {},
   "source": [
    "- If your team is investing heavily in developing LLM applications, [reach out](mailto:endpoints-help@anyscale.com) to us to learn more about how [Ray](https://github.com/ray-project/ray) and [Anyscale](http://anyscale.com/) can help you scale and productionize everything.\n",
    "- Start serving (+fine-tuning) OSS LLMs with [Anyscale Endpoints](https://www.anyscale.com/endpoints) ($1/M tokens for Llama-2-70b) w/ 1M free tokens trial.\n",
    "- If you need to deploy on your own private cloud, check out [Anyscale Private Endpoints](https://www.anyscale.com/endpoints#private).\n",
    "- Learn more about how companies like OpenAI, Netflix, Pinterest, Verizon, Instacart and others leverage Ray and Anyscale for their AI workloads at the [Ray Summit](https://raysummit.anyscale.com/).\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
